#! /usr/bin/env bash

# x11docker
# Run GUI applications and desktop environments in Docker containers.
#
# - Runs additional X servers to circumvent common X security leaks.
# - Restricts container capabilities to enhance container security.
# - Container user is same as host user to avoid root in container.
# - Features e.g. sound, hardware acceleration and data storage.
#
# Run 'x11docker --help' or scroll down to read usage information.
# More documentation at:  https://github.com/mviereck/x11docker

Version="7.1.4-beta-1"

# --enforce-i: Enforce running in interactive mode to allow commands tty and weston-launch in special setups.
grep -q -- "--enforce-i" <<< "$*" && case $- in
  *i*) set +H ;;
  *)   exec bash --noprofile --norc --noediting -i -- "$0" "$@" ;;
esac

usage() {                       # --help: show usage information
  echo "
x11docker: Run GUI applications and desktop environments in containers.
           Supports docker, podman and nerdctl.

Usage:
To run a container on a new X server:
  x11docker IMAGE
  x11docker [OPTIONS] IMAGE [COMMAND]
  x11docker [OPTIONS] -- IMAGE [COMMAND [ARG1 ARG2 ...]]
  x11docker [OPTIONS] -- CUSTOM_RUN_OPTIONS -- IMAGE [COMMAND [ARG1 ARG2 ...]]
To run a host application on a new X server:
  x11docker [OPTIONS] --backend=host COMMAND
  x11docker [OPTIONS] --backend=host -- COMMAND [ARG1 ARG2 ...]
  x11docker [OPTIONS] --backend=host -- -- COMMAND [ARG1 ARG2 ...] -- [ARG3]
To run only an empty new X server:
  x11docker [OPTIONS] --xonly

x11docker always runs a fresh container from image and discards it afterwards.
Runs on Linux and (with some restrictions) on MS Windows. Not adapted for macOS.

Optional features:
  * GPU hardware acceleration
  * Sound with pulseaudio or ALSA
  * Clipboard sharing
  * Printer access
  * Webcam access
  * Persistent home folder
  * Wayland support
  * Language locale creation
  * Several init systems and DBus in container
  * Support of several container runtimes and backends
Focus on security:
  * Avoids X security leaks using additional X servers.
  * Container user is same as host user to avoid root in container.
  * Restricts container capabilities to bare minimum.
To switch between docker, podman and nerdctl use option --backend.

x11docker sets up an unprivileged container user with password 'x11docker'
and restricts container capabilities. Some applications might behave different
than with a regular 'docker run' command due to these security restrictions.
Achieve a less restricted setup with --cap-default or --sudouser.

Dependencies on host:
  For core functionality x11docker only needs bash, an X server and one of
  docker, podman or nerdctl.
  Depending on chosen options x11docker might need some additional tools.
  It checks for them on startup and shows messages if some are missing.
  Core list of recommended tools:
   * Most recommended: Provide image x11docker/xserver to run X in container.
  Otherwise provide on host:
   * Recommended to allow security and convenience:
       X servers: xpra Xephyr nxagent
       X tools:   xauth xclip xrandr xhost xinit
   * Advanced GPU support: weston Xwayland xpra xdotool
  See also: https://github.com/mviereck/x11docker/wiki/Dependencies

Dependencies in image:
  No dependencies in image except for a few feature options. Most important:
   --gpu:  OpenGL/MESA packages, collected often in 'mesa-utils' package.
   --pulseaudio: Needs pulseaudio on host and pulseaudio client libs in image.
   --printer: Needs cups on host and cups client libs in image.
  See also: https://github.com/mviereck/x11docker/wiki/Dependencies

Options: (short options do not accept arguments)
     --help            Display this message and exit.
     --license         Show license of x11docker (MIT) and exit.
     --version         Show x11docker version and exit.

Basic settings:
 -d, --desktop         Indicate a desktop environment in image.
 -i, --interactive     Run with an interactive tty to allow shell commands.
                       Useful with commands like bash.
     --backend=BACKEND  Container backend to use. BACKEND can be:
                         docker  (recommended for rootful) (default)
                         podman  (recommended for rootless and rootful)
                         nerdctl
                       Other backends: (no real containers)
                         host   Run a host application.
                         proot  Run in a rootfs file system. (rootless)
                                Either specify path to rootfs as IMAGENAME,
                                or provide one to call with 'image-name' at
                                ~/.local/share/x11docker/ROOTFS/image-name
                       Tool 'image2rootfs' helps to create a rootfs from docker
                       images: https://github.com/mviereck/image2rootfs
     --xc [=yes|no|BACKEND]  Run X server in container of x11docker/xserver.
                       BACKEND can specify one of docker|podman|nerdctl.
     --xonly           Only start empty X server.

Host integration:
     --alsa [=ALSA_CARD]  Sound with ALSA. You can define a desired sound card
                       with ALSA_CARD. List of available sound cards: aplay -l
 -c, --clipboard       Share clipboard. Graphical clips with --xpra only.
 -g, --gpu [=yes|no|iglx|virgl] GPU access for hardware accelerated OpenGL.
                       Works best with open source drivers on host and in image.
                       For closed source nvidia drivers regard terminal output.
                       Direct rendering supported by few X server options only.
                       Arg 'iglx' enables indirect rendering (--xorg only).
                       Arg 'virgl' allows GPU access for all X servers, but
                       with limited performance and with --xc only.
 -I, --network [=NET]  Allow internet access.
                       For optional argument NET see Docker documentation
                       of docker run option --network.
 -l, --lang [=LOCALE]  Set language variable LANG=LOCALE in container.
                       Without arg LOCALE host variable --lang=\$LANG is used.
                       If LOCALE is missing in image, x11docker generates it
                       with 'localedef' in container (needs 'locales' package).
                       Examples for LOCALE: ru, en, de, zh_CN, cz, fr, fr_BE.
 -P, --printer [=MODE] Share host printers through CUPS server.
                       Optional MODE can be 'socket' or 'tcp'. Default: socket
 -p, --pulseaudio [=MODE]  Sound with pulseaudio. Needs 'pulseaudio' on host
                       and in image. Optional arg MODE can be 'socket', 'tcp'
                       or 'host'. tcp mode needs network access with --network.
     --webcam          Share host webcam device files.

Shared host folders or volumes:
 -m, --home [=ARG]     Create a persistent HOME folder for data storage.
                       Default: Uses ~/.local/share/x11docker/IMAGENAME.
                       ARG can be another host folder or a volume.
                       (~/.local/share/x11docker has a softlink to ~/x11docker.)
                       (Use --homebasedir to change this base storage folder.)
     --share=ARG       Share host file or folder ARG. Read-only with ARG:ro
                       Device files in /dev can be shared, too.
                       ARG can also be a volume instead of a host folder.

X server options:
     --auto            Automatically choose X server (default). Influenced
                       notably by options --desktop, --gpu, --wayland, --wm.
 -h, --hostdisplay     Share host display :0. Quite bad container isolation!
                       Least overhead of all X server options.
                       Some apps may fail due to restricted untrusted cookies.
                       Remove restrictions with option --clipboard.
 -a, --xpra            Nested X server supporting seamless and --desktop mode.
     --xpra2           Like --xpra --xc, but runs xpra client on host.
 -A, --xpra-xwayland   Like --xpra, but supports option --gpu.
     --xpra2-xwayland  Like --xpra2, but supports option --gpu.
 -n, --nxagent         Nested X server supporting seamless and --desktop mode.
                       Faster than --xpra, but can have compositing issues.
 -y, --xephyr          Nested X server for --desktop mode. Without --desktop
                       a host window manager will be provided (option --wm).
 -Y, --weston-xwayland Desktop mode like --xephyr, but supports option --gpu.
                       Runs from console, within X and within Wayland.
 -x, --xorg            Core Xorg server. Runs ootb from console.
                       Switch tty with <CTRL><ALT><F1>....<F12>. Always switch
                       to a black tty before switching to X to avoid crashes.

Special X server options:
 -t, --tty             Terminal only mode. Does not run an X or Wayland server.
     --xvfb            Invisible X server using Xvfb.
                       Can be used for custom access with xpra or VNC.
 -X, --xwayland        Blanc Xwayland, needs a running Wayland compositor.
     --xwin            X server to run in Cygwin/X on MS Windows.
     --runx            X server wrapper for VcXsrv and Xwin on MS Windows.

Wayland instead of X:
 -W, --wayland         Automatically set up a Wayland environment.
                       Chooses one of following options and regards --desktop.
 -T, --weston          Weston without X for pure Wayland applications.
                       Runs in X, in Wayland or from console.
 -K, --kwin            KWin without X for pure Wayland applications.
                       Runs in X, in Wayland or from console.
 -H, --hostwayland     Share host Wayland without X for pure Wayland apps.

X and Wayland appearance options:
     --border [=COLOR] Draw a colored border in windows of --xpra[-xwayland].
                       Argument COLOR can be e.g. 'orange' or '#F00'. Thickness
                       can be specified, too, e.g. 'red,3'. Default: 'blue,1'
     --dpi=N           dpi value (dots per inch) to submit to X clients.
                       Influences font size of some applications.
 -f, --fullscreen      Run in fullscreen mode.
     --output-count=N  Multiple virtual monitors for Weston, KWin or Xephyr.
     --rotate=N        Rotate display (--xorg, --weston and --weston-xwayland)
                       Allowed values: 0, 90, 180, 270, flipped, flipped-90,
                       flipped-180, flipped-270.  (flipped means mirrored)
     --scale=N         Scale/zoom factor N for xpra, Xorg or Weston.
                       Allowed for --xpra, --xorg --xpra-xwayland: 0.25...8.0.
                       Allowed for --weston and --weston-xwayland: 1...9.
                       (Mismatching font sizes can be adjusted with --dpi).
                       Odd resolutions with --xorg might need --scale=1.
     --size=WxH        Screen size of new X server (e.g. 800x600).
 -w, --wm [=ARG]       Provide a host window manager to container applications.
                       Possible ARG:
                         host: autodetection of a host window manager.
                         COMMAND: command of a desired host window manager.
                         none: Run without a window manager. Same as --desktop.
 -F, --xfishtank       Show fish tank on new X server.

X and Wayland special configuration:
     --clean-xhost     Disable xhost access policies on host display.
     --composite [=yes|no]  Enable or disable X extension Composite.
                       Default is yes except for --nxagent. Can cause or
                       fix issues with some applications on nxagent.
     --display=N       Use display number N for new X server.
     --keymap=LAYOUT   Set keyboard layout for new X server, e.g. de, us, ru.
                       For possible LAYOUT look at /usr/share/X11/xkb/symbols.
     --no-auth         Allow access to X for everyone. Security risk!
     --vt [=N]         Use vt / tty N. Regarded by --xorg only.
                       Without N automatically search an unused tty.
     --westonini=FILE  Custom weston.ini for --weston and --weston-xwayland.
     --xhost [=STR]    Set \"xhost STR\" on new X server (see 'man xhost').
                       Without STR will set:  +SI:localuser:\$USER
                       (Use with care. '--xhost=+' allows access for everyone).
     --xoverip         Connect to X over TCP network. For special setups only,
                       usually only enabled by x11docker itself.
                       Only supported by a subset of X server options.
                       Needs option --network.
     --xtest [=yes|no] Enable or disable X extension XTEST. Default is yes for
                       --xpra and --xvfb, no for other X servers.
                       Needed to allow custom access with xpra.

Container user settings:
     --group-add=GROUP Add container user to group GROUP.
     --hostuser=USER   Run X (and container user) as user USER. Default is
                       result of \$(logname). (x11docker must run as root).
     --password [=WORD]   Change container user password and exit.
                       Interactive input if argument WORD is not provided.
                       Stored encrypted in ~/.config/x11docker/passwd.
     --sudouser [=nopasswd] Allow su and sudo for container user. Use with care,
                       severe reduction of default x11docker security!
                       Optionally passwordless sudo with argument nopasswd.
                       Default password is 'x11docker'.
     --user=N          Create container user N (N=name or N=uid). Default:
                       same as host user. N can also be an unknown user id.
                       You can specify a group id with N being 'user:gid'.
                       Special case: --user=RETAIN keeps image user settings.

Container capabilities:
  In most setups x11docker sets --cap-drop=ALL --security-opt=no-new-privileges
  and shows warnings if doing otherwise.
  Custom capabilities can be added with --cap-add=CAP after  --
     --cap-default     Allow default container capabilities.
                       Includes --newprivileges=yes.
     --ipc [=ARG]      Without ARG sets run option --ipc=host. (Discouraged)
                       For other possible ARG see docker run reference.
     --limit [=FACTOR] Limit CPU and RAM usage of container to
                       currently free RAM x FACTOR and available CPUs x FACTOR.
                       Allowed range is 0 < FACTOR <= 1.
                       Default for --limit without argument FACTOR: 0.5
     --newprivileges [=yes|no|auto]  Set or unset run option
                       --security-opt=no-new-privileges. Default with no
                       argument is 'yes'. Default for most cases is 'no'.

Container init system, elogind and DBus daemon:
     --dbus [=system]  Run DBus user session daemon for container command.
                       With argument 'system' also run a DBus system daemon.
                       (To run a DBus system daemon rather use one of
                        --init=systemd|openrc|runit|sysvinit )
     --hostdbus        Connect to DBus user session from host.
     --init [=INITSYSTEM] Run an init system as PID 1 in container. Solves the
                       zombie reaping issue. INITSYSTEM can be:
                         tini: Default. Mostly present as docker-init on host.
                         none: No init system, container command will be PID 1.
                       Others: systemd, sysvinit, runit, openrc, s6-overlay.
     --sharecgroup     Share /sys/fs/cgroup. Allows elogind in container if
                       used with one of --init=openrc|runit|sysvinit

Container special configuration:
     --env VAR=value   Set custom environment variable VAR=value
     --name=NAME       Specify container name NAME.
     --no-entrypoint   Disable ENTRYPOINT in image to allow other commands, too
     --no-setup        No x11docker setup in running container. Disallows
                       several other options. See also --user=RETAIN.
     --runtime=RUNTIME  Specify container runtime. Known by x11docker:
                         runc:         Docker default runtime.
                         crun:         Fast replacement for runc written in C.
                         nvidia:       Runtime for nvidia/nvidia-docker images.
                         kata-runtime: Runtime using a QEMU VM.
                         sysbox-runc:  Runtime for powerful root in container.
     --shell=SHELL     Set preferred user shell. Example: --shell=/bin/zsh
     --snap            Enable support for Docker in snap.
     --stdin           Forward stdin of x11docker to container command.
     --workdir=DIR     Set working directory DIR.

Additional commands: (You might need to move them to background with 'CMD &'.)
     --runasroot=CMD   Run command CMD as root in container.
     --runasuser=CMD   Run command CMD with user privileges in container
                       before running image command.
     --runfromhost=CMD Run host command CMD on new X server.

Miscellaneous:
     --build IMAGE     Build an image from a Dockerfile from x11docker
                       repository. Example: 'x11docker --build x11docker/fvwm'
                       Works for all repositories beginning with 'dockerfile'
                       at https://github.com/mviereck?tab=repositories
                       Regards (only) option --backend=BACKEND.
     --cachebasedir=DIR  Custom base folder for cache files.
     --homebasedir=DIR   Custom base folder for option --home.
     --enforce-i       Run x11docker in interactive bash mode to allow tty
                       access. Can help to run weston-launch on special systems.
     --fallback [=yes|no]  Allow or deny fallbacks if a chosen option cannot
                       be fulfilled. By default fallbacks are allowed.
     --launcher        Create application launcher with current options
                       on desktop and exit. You can get a menu entry moving
                       the created .desktop file to ~/.local/share/applications
     --mobyvm          Use MobyVM (for WSL2 only that defaults to Linux Docker).
     --preset=FILE     Read a set of predefined options stored in file FILE.
                       Useful to shortcut often used option combinations.
                       FILE is searched in directory /etc/x11docker/preset,
                       or in directory ~/.config/x11docker/preset.
                        - Multiple lines in FILE are allowed.
                        - Comment lines must begin with #
                        - Local presets supersede global ones in /etc
                       Special case: A preset file with file name 'default'
                       will be applied automatically for all x11docker sessions.

Output of parseable information on stdout:
  Get output e.g. with: read xenv < <(x11docker --printenv x11docker/check)
  Optional argument FILE allows to print the information into a file.
     --printenv [=FILE]        Print variables to access new display.
     --printid [=FILE]         Print container ID.
     --printinfofile [=FILE]   Print path to internal x11docker info storage.
     --printpid1 [=FILE]       Print host PID of container PID 1.

Verbosity options:
 -D, --debug           Debug mode: Show some less verbose debug output
                       and enable rigorous error control.
 -q, --quiet           Suppress x11docker terminal messages.
 -v, --verbose         Be verbose. Output of x11docker.log on stderr.
 -V                    Be verbose with colored output.

Installation options and cleanup (need root permissions):
     --install         Install x11docker from current folder.
                       Useful to install from an extracted zip file.
     --update          Download and install latest release from github.
     --update-master   Download and install latest master version from github.
     --remove          Remove x11docker from your system. Includes --cleanup.
                       Preserves ~/.local/share/x11docker from option --home.
     --cleanup         Clean up orphaned containers and cache files.
                       Terminates currently running x11docker containers, too.
                       Regards (only) option --backend=BACKEND.
--update, --update-master and --remove regard a possible custom installation
path different from default /usr/bin directory.
Additional options are disregarded.

Exit codes:
  0:     Success
  64:    x11docker error
  130:   Terminated by ctrl-c
  other: Exit code of command in container

x11docker version: $Version
Please report issues and get help at: https://github.com/mviereck/x11docker
"
}
license() {                     # --license: show license (MIT)
echo 'MIT License

Copyright (c) 2015, 2016, 2017, 2018, 2019, 2020, 2021, 2022 Martin Viereck

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.'
}

#### messages
alertbox() {                    # X alert box with title $1 and message $2
  local Title Message
  Title="${1:-}"
  Message="${2:-}"

  Message="$(echo "$Message" | LANG=C sed "s/[\x80-\xFF]//g" | fold -w120 )" # remove UTF-8 special chars; line folding at 120 chars

  # try some tools to show alert message. If all tools fail, return 1
  command -v   xmessage     >/dev/null && [ -n "${DISPLAY:-}" ] && {
    echo "$Title

$Message" | xmessage  -file - -default okay ||:
  } || {
    command -v xmessage    >/dev/null && [ -n "${DISPLAY:-}" ] && {
      echo "$Title

$Message" | gxmessage -file - -default okay ||:
    }
  } || {
    command -v zenity       >/dev/null && [ -n "${DISPLAY:-}" ] && {
      zenity --error --no-markup --ellipsize --title="$Title" --text="$Message" 2>/dev/null ||:
    }
  } || {
    command -v yad          >/dev/null && [ -n "${DISPLAY:-}" ] && {
      yad  --image "dialog-error" --title "$Title" --button=gtk-ok:0 --text "$(echo "$Message" | sed 's/\\/\\\\/g')" --fixed 2>/dev/null ||:
    }
  } || {
    command -v kdialog      >/dev/null && [ -n "${DISPLAY:-}" ] && {
      kdialog --title "$Title" --error "$(echo "$Message" | sed 's/\\/\\\\/g' )" 2>/dev/null ||:
    }
  } || {
    command -v xterm        >/dev/null && [ -n "${DISPLAY:-}" ] && {
      xterm -title "$Title" -e "echo '$(echo "$Message" | sed "s/'/\"/g")' ; read -n1" ||:
    }
  } || {
    notify-send "$Title:

$Message" 2>/dev/null
  } || {
    warning "Could not display message on X:
$Message"
    return 1
  }
  return 0
}
debugnote() {                   # show debug output $*
  [ "$Debugmode" = "yes" ] && [ "$Verbose" != "yes" ] && echo "${Colblue}DEBUGNOTE[$(timestamp)]:${Colnorm} $*" >&${FDstderr}
  logentry "DEBUGNOTE[$(timestamp)]: $*"
  return 0
}
error() {                       # show error message and exit
  local Message

  break >/dev/null 2>&1 # just in case error occured in a loop

  Message="$*

  Type 'x11docker --help' for usage information
  Debug options: '--verbose' (full log) or '--debug' (log excerpt).
  Logfile will be: $Logfilebackup
  Please report issues at https://github.com/mviereck/x11docker"

  Message="$(rmcr <<< "$Message")"

  # output to terminal
  echo -e "
${Colredbg}x11docker ERROR:${Colnorm} $Message
" >&2

  # output to logfile
  logentry "x11docker ERROR: $Message
"
  saygoodbye error
  storeinfo test error && waitfortheend
  storeinfo error=64

  # output to X dialogbox if not running in terminal
  [ "$Runsinterminal" = "no" ] && [ "$Silent" = "no" ] && export ${Hostxenv:-DISPLAY} && alertbox "x11docker ERROR" "$Message" &

  finish
}
logentry() {                    # write into logfile
  [ -e "$Logfile" ] && {
    [ -n "$Logmessages" ] && echo "$Logmessages" >> "$Messagelogfile" 2>/dev/null && Logmessages=""
    echo "$*" >> "$Messagelogfile" 2>/dev/null
    :
  } || Logmessages="$Logmessages
$*"
}
note() {                        # show notice messages
  [ "$Verbose" = "yes" ] || echo "${Colgreen}x11docker note:${Colnorm} $*
" >&${FDstderr}
  logentry "x11docker note: $*
"
}
traperror() {                   # trap ERR: --debug: Output for 'set -o errtrace'
  debugnote "traperror: Command at Line ${2:-} returned with error code ${1:-}:
  ${4:-}
  ${3:-} - ${5:-}"
  storeinfo error=64
  saygoodbye traperror
}
verbose() {                     # show verbose messages
  # only logfile notes here, terminal output is done with tail in setup_verbosity()
  logentry "x11docker[$(timestamp)]: $*
"
}
warning() {                     # show warning messages
  [ "$Verbose" = "yes" ] || echo "${Colyellow}x11docker WARNING:${Colnorm} $*
" >&${FDstderr}
  logentry "x11docker WARNING: $*
"
}
watchmessagefifo() {            # watch for messages coming from inside of container
  # message in fifo must end with :$Messagetype
  local Line= Message= Messagetype=
  trap '' SIGINT
  while [ -e "$Cachefolder" ]; do
    IFS= read -r Line <&${FDmessage} ||:
    [ "$Line" ] || sleep 2  # sleep for MSYS2/CYGWIN workaround
    [ "$Line" ] && Message="$Message
$Line"
    grep -q -E ":WARNING|:NOTE|:DEBUGNOTE|:VERBOSE|:ERROR|:STDOUT" <<< "$Line" && {
      Messagetype=":$(echo "$Line" | rev | cut -d: -f1  | rev | tr -d ' ')"
      Message="${Message%$Messagetype}"
      Message="$(tail -n +2 <<< "$Message")" # remove leading newline
      case "$Messagetype" in
        :WARNING)   warning   "$Message" ;;
        :NOTE)      note      "$Message" ;;
        :DEBUGNOTE) debugnote "$Message" ;;
        :ERROR)     error     "$Message" ;;
        :VERBOSE)   [ "-d " = "$(cut -c1-3 <<<"$Message" | head -n1)" ] && debugnote "$(tail -c +4 <<< "$Message")" || verbose "$Message" ;;
        :STDOUT)    echo "$Message" ;;
      esac
      Message=
      Messagetype=
    }
  done
}

#### exit
finish() {                      # trap EXIT routine to clean up background processes and cache
  local Pid Name Zeit Exitcode Pid1pid= Watchmessagefifopid= Count Xpid1pid

  # do not finish() in subshell, just give signal to all other processes and terminate subshell
  [ "$$" = "$BASHPID" ] || {
    saygoodbye finish-subshell
    exit 0
  }

  debugnote "Terminating x11docker."
  saygoodbye "finish"
  trap - EXIT
  trap - ERR
  trap - SIGINT

  while read -r Line ; do

    Pid="$(echo "$Line"  | awk '{print $1}')"
    Name="$(echo "$Line" | awk '{print $2}')"
    debugnote "finish(): Checking pid $Pid ($Name): $(pspid "$Pid" || echo '(already gone)')"

    checkpid "$Pid" && {
      case "$Name" in
        watchmessagefifo) ;;
        containerpid1)
          Pid1pid="$Pid"
          #[ "$Winsubsystem" ] && Pid1pid=""
          termpid "$Pid1pid" "$Name" || Debugmode="yes"
          # Give container time for graceful shutdown
          for Count in 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0; do
            checkpid "$Pid1pid" || break
            mysleep "$(awk "BEGIN { print $Count * 0.1 }")"
            debugnote "finish(): Waiting for container PID 1: $Pid1pid to terminate."
          done
          checkpid "$Pid1pid" && $Backendbin stop "$Xcontainername"
        ;;
        Xcontainerpid1)
          Xpid1pid="$Pid"
          #[ "$Winsubsystem" ] && Pid1pid=""
          termpid "$Xpid1pid" "$Name" || Debugmode="yes"
          # Give container time for graceful shutdown
          for Count in 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0; do
            checkpid "$Xpid1pid" || break
            mysleep "$(awk "BEGIN { print $Count * 0.1 }")"
            debugnote "finish(): Waiting for X container PID 1: $Xpid1pid to terminate."
          done
        ;;
        *)
          termpid "$Pid" "$Name"
        ;;
      esac
    }
  done < <(tac "$Storepidfile" 2>/dev/null)

  # --pulseaudio: unload module
  Pulseaudiomoduleid="$(storeinfo dump pulseaudiomoduleid)"
  [ "$Pulseaudiomoduleid" ] && unpriv "pactl unload-module '$Pulseaudiomoduleid'"

  # Check if container is still running -> docker stop
  case "$Backend" in
    docker|podman|nerdctl)
      containerisrunning && {
        Debugmode="yes"
        debugnote "finish(): Container still running. Executing 'docker stop'.
  Will wait up to 15 seconds for docker to finish."
        $Backendbin stop "$Containername" >> "$Containerlogfile" 2>&1

        Zeit="$(date +%s)"
        while :; do
          containerisrunning || break
          debugnote "finish(): Waiting for container to terminate ..."
          sleep 1
          [ 15 -lt $(($(date +%s) - $Zeit)) ] && break
        done

        containerisrunning && {
          Exitcode="64"
          debugnote "finish(): Container did not terminate as it should.
  Will not clean cache to avoid file permission issues.
  You can remove the new container with command:
    docker rm -f $Containername
  Afterwards, remove cache files with:
    rm -R $Cachefolder
  or let x11docker do the cleanup work for you:
    x11docker --cleanup"
          Preservecachefiles="yes"
        } || debugnote "finish(): Container terminated successfully"
      }

      # remove container
      [ "$Preservecachefiles" = "no" ] && [ "$Containername" ] && {
        debugnote "Removing container $Containername
        $($Backendbin rm -f "$Containername" 2>&1)"
      }
    ;;
    chroot)
      while read -r Line; do
        debugnote "--backend=$Backend: umount $Line"
        Source="$(cut -d'|' -f1 <<< "$Line")"
        Target="$(cut -d'|' -f2 <<< "$Line")"
        [ -d "$Source" ] && {
          umount -f "$Target" || warning "umount failed on '$Target'"
        }
        [ -S "$Source" ] && rm "$Target"
        [ -f "$Source" ] && rm "$Target"
      done <<< "$Chrootmountlist"
      #umount -t devpts "$Rootfs/dev/pts" || error "--backend=$Backend: umount of devpts failed."
    ;;
  esac

  # Stop watching for messages, check others again
  while read -r Line ; do
    Pid="$(echo "$Line"  | awk '{print $1}')"
    Name="$(echo "$Line" | awk '{print $2}')"
    checkpid "$Pid" && termpid "$Pid" "$Name"
    checkpid "$Pid" && {
      # should never happen
      warning "Failed to terminate pid $Pid ($Name): $(pspid "$Pid" ||:)"
      storeinfo error=64
    }
  done < <(tac "$Storepidfile" 2>/dev/null)

  Exitcode=$(storeinfo dump error)
  Exitcode="${Exitcode:-0}"
  debugnote "x11docker exit code: $Exitcode"
  storeinfo test cmdexitcode && {
    Exitcode=$(storeinfo dump cmdexitcode)
    debugnote "CMD exit code: $Exitcode"
  }

  # backup of logfile in $Cachebasefolder
  [ -e "$Logfile" ] && {
    [ "$Verbose" ] && sleep 1
    unpriv "cp '$Logfile' '$Logfilebackup'"
    case "$Winsubsystem" in
      WSL1|WSL2)
        [ "$Mobyvm" = "yes" ] && unpriv "cp -T '$Logfilebackup' '$Hostuserhome/.cache/x11docker/x11docker.log'"
      ;;
    esac
    #unpriv "rmcr '$Logfilebackup'"
  }

  # softlink to X unix socket in container
  case "$Xcontainer" in
    yes)
      [ -L "/tmp/.X11-unix/X$Newdisplaynumber" ]          && rm "/tmp/.X11-unix/X$Newdisplaynumber"
      [ -L "$XDG_RUNTIME_DIR/wayland-$Newdisplaynumber" ] && rm "$XDG_RUNTIME_DIR/wayland-$Newdisplaynumber"
    ;;
  esac
  
  # close file descriptors
  mysleep 0.2
  for Descriptor in ${FDcmdstdin} ${FDmessage} ${FDstderr} ${FDtimetosaygoodbye} ${FDwatchpid} ; do
    exec {Descriptor}>&-
  done

  # remove cache files
  [ "$Preservecachefiles" = "no" ] && grep -q cache <<< "$Cachefolder" && grep -q x11docker <<< "$Cachefolder" && [ "x11docker" != "$(basename "$Cachefolder")" ] && unpriv "rm -f -R '$Cachefolder'"

  case "$Runssourced" in
    yes) return "$Exitcode" ;;
    *)   exit   "$Exitcode" ;;
  esac
}
finish_sigint() {               # trap SIGINT to activate debug mode on finish()
  local Pid1pid
  Debugmode="yes"
  debugnote "Received SIGINT"
  storeinfo error=130
  finish
}
saygoodbye() {                  # create file signaling watching processes to terminate
  debugnote "time to say goodbye ($*)"
  [ -e "$Timetosaygoodbyefile" ] && echo timetosaygoodbye >> "$Timetosaygoodbyefile"
  [ -e "$Timetosaygoodbyefifo" ] && echo timetosaygoodbye >> "$Timetosaygoodbyefifo"
}

#### watching processes
checkpid() {                    # check if PID $1 is active
  #ps -p ${1:-} >/dev/null 2>&1
  [ -e "/proc/${1:-NONSENSE}" ]
}
containerisrunning() {          # check if container is running
  storeinfo test containerid || return 1
  case "$Mobyvm" in
    no)   checkpid      "$(storeinfo dump pid1pid)" ;;
    yes)  $Backendbin inspect "$(storeinfo dump containerid)" >/dev/null 2>&1 ;;
  esac
}
pspid() {                       # ps -p $1 --no-headers
  # On some systems ps does not have option --no-headers.
  # On some systems (busybox) ps -p is not supported  ### FIXME
  # return 1 if not found
  LC_ALL=C ps -p "${1:-}" 2>/dev/null | grep -v 'TIME'
}
rocknroll() {                   # check whether x11docker session is still running
  [ -s "$Timetosaygoodbyefile" ]   && return 1
  [ -e "$Timetosaygoodbyefile" ]   || return 1
  return 0
}
setonwatchpidlist() {           # add PID $1 to watchpidlist()
  debugnote "watchpidlist(): Setting pid ${1:-} on watchlist: ${2:-}"
  echo "${1:-}" >> "$Watchpidfifo"
  # add to list of background processes
  grep -q CONTAINER <<<  "${1:-}" || storepid "${1:-}" "${2:-}"
}
storepid() {                    # store pid $1 and name $2 of process in file $Storepidfile.
  # Store pid and process name of background processes in a file
  # Used in finish() to clean up background processes
  # Store:
  #  $1 Pid
  #  $2 codename
  # Test for stored pid or codename:
  #  $1 test
  #  $2 pid or codename
  # Dump stored pid:
  #  $1 dump
  #  $2 codename

  case "${1:-}" in
    dump) grep    -w "${2:-}" "$Storepidfile" | cut -d' ' -f1 ;;
    test) grep -q -w "${2:-}" "$Storepidfile" ;;
    *)
      echo "${1:-NOPID}" "${2:-NONAME}" >> "$Storepidfile"
      debugnote "storepid(): Stored pid '${1:-}' of '${2:-}': $(pspid "${1:-}" ||:)"
    ;;
  esac
}
termpid() {                     # kill PID $1 with codename $2
  # TERM
  debugnote "termpid(): Terminating ${1:-} (${2:-}): $(pspid "${1:-}" ||:)"
  checkpid "${1:-}" && {
    kill "${1:-}" 2>/dev/null
    :
  } || return 0
  mysleep 0.1
  checkpid "${1:-}" && mysleep 0.4 || return 0

  # KILL
  debugnote "termpid(): Killing ${1:-} (${2:-}): $(pspid "${1:-}" ||:)"
  checkpid "${1:-}" && kill -s KILL "${1:-}" 2>/dev/null
  mysleep 0.2
  checkpid "${1:-}" && {
    note "Failed to terminate ${1:-} (${2:-}): $(ps -u -p "${1:-}" 2>/dev/null | tail -n1)"
    return 1
  }

  return 0
}
waitfortheend() {               # wait for end of x11docker session
  # signal is byte in $Timetosaygoodbyefifo
  # decent read to wait for signal to terminate
  case "$Usemkfifo" in
    yes)
      while rocknroll; do
        bash -c "read -n1 <${FDtimetosaygoodbye}" && saygoodbye timetosaygoodbyefifo || sleep 1
      done
    ;;
    no) # Reading from fifo fails on Windows, workaround
      while rocknroll; do
        sleep 2
      done
    ;;
  esac
  return 0
}
watchpidlist() {                # watch list of important pids
  # terminate x11docker if a PID in $Watchpidlist terminates
  # serves mainly watching X server, Wayland compositor, container and hostexe
  # echo PIDs to watch into >{FDwatchpid} (setonwatchpidlist())
  local Pid= Containername= Line= Watchpidlist=
  trap '' SIGINT

  while rocknroll; do
    # check for new Pid once a second
    read -t1 Pid <&${FDwatchpid} ||:
    [ "$Usemkfifo" = "no" ] && sleep 2  # read does not wait if not a fifo
    # Got new pid
    [ "$Pid" ] && {
      [ "${Pid:0:9}" = "CONTAINER" ] && {
        # Workaround for MS Windows where the pid cannot be watched
        Containername="${Pid#CONTAINER}"
        debugnote "watchpidlist(): Watching Container: $Containername"
      } || {
        Watchpidlist="$Watchpidlist $Pid"
        debugnote "watchpidlist(): Watching pids:
$(for Line in $Watchpidlist; do pspid "$Line" || echo "(pid $Line not found)" ; done)"
      }
    }
    # check all stored pids
    for Pid in $Watchpidlist; do
      [ -e "/proc/$Pid" ] || {
        debugnote  "watchpidlist(): PID $Pid has terminated"
        saygoodbye "watchpidlist $Pid"
      }
    done
    # Container PID not watchable in MSYS2/Cygwin/WSL1.
    [ "$Containername" ] && {
      $Backendbin inspect "$Containername" >/dev/null || {
        debugnote "watchpidlist(): Container $Containername has terminated"
        saygoodbye "watchpidlist $Containername"
      }
    }
  done
  saygoodbye "watchpidlist"
}

#### more or less general routines
askyesno() {                    # ask Yes/no question. Default 'yes' for ENTER, timeout with 'no' after 60s
  local Choice
  read -t60 -n1 -p "(timeout after 60s assuming no) [Y|n]" Choice
  [ "$?" = '0' ] && {
    [[ "$Choice" == [YyJj]* ]] || [ -z "$Choice" ] && return 0
  }
  return 1
}
check_envvar() {                # allow only chars in string $1 that can be expected in environment variables
  # Allows only chars in "a-zA-Z0-9_:/.,@=-"
  # Option -w allows whitespace, too. Can be needed for PATH.
  # Char * as in LS_COLORS is not allowed to avoid abuse.
  # Replaces forbidden chars with X and returns 1
  # Returns 0 if no change occurred.
  # Echoes result.
  local Newvar Space=

  case "${1:-}" in
    -w) Space=" " ; shift ;;
  esac

  Newvar="$(printf %s "${1:-}" | LC_ALL=C tr -c "a-zA-Z0-9_:/.,@=${Space}-" "X" )"

  printf %s "$Newvar"
  printf "\n"

  [ "$Newvar" = "${1:-}" ] && return 0

  debugnote "check_envvar(): Input string has been changed. Result:
  $Newvar"
  return 1
}
check_parent_sshd() {           # check whether pid $1 runs in SSH session
  local Wanted_pid="${1:-}" Process_line
  local Return
  ps -p 1 >/dev/null 2>&1 || {
    debugnote "check_parent_sshd(): Failed to check for sshd. ps -p not supported."
    return 1
  }
  while [ "$Wanted_pid" -ne 1 ] ; do
    Process_line="$(ps -f -p "$Wanted_pid"| tail -n1)"
    Wanted_pid="$(echo "$Process_line" | awk '{print $3}')"
    [[ $Process_line =~ sshd ]] && Return=0
    [ "$Return" ] && break
  done
  return "${Return:-1}"
}
download() {                    # download file at URL $1 and store it in file $2
  # Uses wget or curl. If both are missing, returns 1.
  # With no arguments it checks for curl/wget without downloading.
  # Download follows redirects.
  local Downloader=
  command -v wget >/dev/null && Downloader="wget"
  command -v curl >/dev/null && Downloader="curl"
  [ "$Downloader" ] || return 1
  [ "${1:-}" ]      || return 0
  case "$Downloader" in
    wget) wget    "${1:-}" -O       "${2:-}" ;;
    curl) curl -L "${1:-}" --output "${2:-}" ;;
  esac
  return $?
}
escapestring() {                # escape special chars of $1
  # escape all characters except those described in [^a-zA-Z0-9,._+@=:/-]
  grep -q "'" <<< "${1:-}" && {
    error "escapestring(): x11docker cannot escape char ' in :
  ${1:-}"
    return 1
  }
  echo "${1:-}" | LC_ALL=C sed -e 's/[^a-zA-Z0-9,._+@=:/-]/\\&/g; '
}
getrandomnumber() {             # get random number
  # chosen by fair dice roll
  # guaranteed to be random
  echo "4"
}
isint() {                       # check if $1 is a positive integer
  #[[ $var =~ ^-?[0-9]+$ ]]
  [[ "${1:-}" =~ ^[0-9]+$ ]]
}
isnum() {                       # check if $1 is a number
  [ "1" = "$(awk -v a="${1:-}" 'BEGIN {print (a == a + 0)}')" ]
}
makecookie() {                  # bake a cookie
  mcookie 2>/dev/null || echo $RANDOM$RANDOM$RANDOM$RANDOM$RANDOM$RANDOM | cut -b1-32
}
mysleep() {                     # catch cases where sleep only supports integer
  sleep "${1:-1}" 2>/dev/null || sleep 1
}
parse_inspect() {               # parse json of inspect output using python
  # parse for keys in output of docker|podman|nerdctl inspect.
  # Uses python json parser.
  # $1 String containing inspect output
  # $2...$n Key. For second level keys provide e.g. "jsonstring" "Config" "Cmd"

  local Parserscript

  Parserscript="$Cachefolder/parse_inspect.py"
  Parserscript="#! $Pythonbin
$(cat << EOF
import json,sys

def parse_inspect(*args):
    """
    parse output of docker|podman|nerdctl inspect
    args:
     0: ignored
     1: string containing inspect output
     2..n: json keys. For second level keys provide e.g. "Config","Cmd"
    Prints key value as a string.
    Prints empty string if key not found.
    A list is printed as a string with '' around each element.
    """

    output=""
    inspect=args[1]
    inspect=inspect.strip()
    if inspect[0] == "[" :
        inspect=inspect[1:-2] # remove enclosing [ ]

    obj=json.loads(inspect)

    for arg in args[2:]: # recursively find the desired object. Command.Cmd is found with args "Command" , "Cmd"
        try:
            obj=obj[arg]
        except:
            obj=""

    objtype=str(type(obj))
    if "'list'" in objtype:
        for i in obj:
            output=output+"'"+str(i)+"' "
    else:
        output=str(obj)

    if output == "None":
        output=""

    print(output)

parse_inspect(*sys.argv)
EOF
  )"
  echo "$Parserscript" | $Pythonbin - "$@"
}
storeinfo() {                   # store some information for later use
  # store and provide pieces of information
  # replace entry if codeword is already present
  # Store as codeword=string:
  #   $1 codeword=string
  # Dump stored string:
  #   $1 dump
  #   #2 codeword
  # Drop stored string:
  #   $1 drop
  #   #2 codeword
  # Test for codeword: (return 1 if not found)
  #   $1 test
  #   $2 codeword
  #
  # note: sed -i causes file permission issues if called in container in Cygwin, compare ticket #187
  #       chmod 666 for $Sharefolder could probably fix that. (FIXME)
  #
  [ -e "$Storeinfofile" ] || return 1
  case "${1:-}" in
    dump) grep     "^${2:-}="   "$Storeinfofile" | sed "s/^${2:-}=//" ;;      # dump entry
    drop) sed -i  "/^${2:-}=/d" "$Storeinfofile" ;;                           # drop entry
    test) grep -q  "^${2:-}="   "$Storeinfofile" ;;                           # test for entry
    *)                                                                        # store entry
      debugnote "storeinfo(): ${1:-}"
      grep -q   "^$(echo "${1:-}" | cut -d= -f1)="     "$Storeinfofile" && {
        sed -i "/^$(echo "${1:-}" | cut -d= -f1)=/d"   "$Storeinfofile"       # drop possible old entry
      }
      echo "${1:-}"                                 >> "$Storeinfofile"
    ;;
  esac
}
rmcr() {                        # remove carriage return to translate DOS/Windows newlines into UNIX newlines
  # convert stdin if $1 is empty. Otherwise convert file $1.
  case "${1:-}" in
    "") sed    "s/$(printf "\r")//g" ;;
    *)  sed -i "s/$(printf "\r")//g"  "${1:-}" ;;
  esac
}
timestamp() {                   # print HH:MM:SS,NNN
  date +%T,%N | cut -c1-12
}
unspecialstring() {             # replace special chars of $1 with -
  # Replace all characters except those described in "a-zA-Z0-9_" with a '-'.
  # Replace newlines, too.
  # Remove leading and trailing '-'
  # Avoid double '--'
  # Return empty string if only special chars are given.
  printf %s "${1:-}" | LC_ALL=C tr -cs "a-zA-Z0-9_" "-" | sed -e 's/^-// ; s/-$//'
}
verlt() {                       # version number check $1 less than $2
  [ "${1:-}" = "${2:-}" ] && return 1 || { verlte "${1:-}" "${2:-}" && return 0 || return 1 ; }
}
verlte() {                      # version number check $1 less than or equal $2
  [  "${1:-}" = "$(echo -e "${1:-}\n${2:-}" | sort -V | head -n1)" ] && return 0 || return 1
}
wincmd() {                      # execute a command on MS Windows with cmd.exe
  MSYS2_ARG_CONV_EXCL='*' cmd.exe /C "${@//&/^&}" | rmcr
}

#### file routines
convertpath() {                 # convert unix and windows paths
  # $1: Mode:
  #     windows   echo Windows path                            - result: c:/path
  #     unix      echo unix path                               - result: /c/path
  #     subsystem echo path within subsystem                   - result: /cygdrive/c/path  or  /path  or /mnt/c/path
  #     volume    echo --volume compatible syntax              - result: 'unixpath':'containerpath':rw  (or ":ro")
  #     container echo path of volume in container             - result: /path
  #     share     echo path of $Sharefolder/file in container  - result: /containerpath
  # $2: Path to convert. Arbitrary syntax, can be C:/path, /c/path, /cygdrive/c/path, /path
  #     Can have suffix :rw or :ro. If none is given, return with :rw
  # $3: Optional for mode volume: containerpath

  local Mode Path Drive= Readwritemode Readwritemode_mount

  Mode="${1:-}"
  Path="${2:-}"

  # check path for suffix :rw or :ro
  Readwritemode="$(echo "$Path" | rev | cut -c1-3 | rev)"
  [ "$(cut -c1 <<< "$Readwritemode")" = ":" ] && {
    Path="$(echo "$Path" | rev | cut -c4- | rev)"
  } || Readwritemode=":rw"
  [ "$Readwritemode" = ":ro" ] && Readwritemode_mount=",readonly" || Readwritemode_mount=""

  # replace ~ with HOME
  Path="$(sed "s%~%${Hostuserhome:-${HOME:-}}%" <<< "$Path")"

  # share: Replace $Sharefolder with $Sharefoldercontainer
  [ "$Mode" = "share" ] && {
    [ -z "$Path" ] && {
      echo ""
      return 0
    }
    echo "${Sharefoldercontainer}${Path#$Sharefolder}"
    return 0
  }

  # replace \ with /
  Path="$(tr '\\' '/' <<< "$Path")"

  # remove possible already given mountpoint
  Path="${Path#$Winsubmount}"

  # Given format is /c/
  [ "$(cut -c1,3 <<< "$Path")" = "//" ] && {
    Drive="$(cut -c2 <<< "$Path")"
    Path="$(cut -c3- <<< "$Path")"
  }

  # Given format is C:/
  [ "$(cut -c2 <<< "$Path")" = ":" ] && {
    Drive="$(cut -c1 <<< "$Path")"
    Path="$(cut -c3- <<< "$Path")"
  }

  # change C to c
  Drive="${Drive,}"

  # docker volume (same for Windows and Unix)
  [ "${Path:0:1}" = "/" ] || {
    case "$Mode" in
      unix|subsystem|windows) echo "$Path" ; debugnote "convertpath() $Mode: Docker volumes do not have a specified path on host: $Path" ;;
      volume)         echo "'$Path':'${3:-/$Path}'${Readwritemode}" ;;
      mount)          echo "type=volume,source='$Path',target='${3:-/$Path}'${Readwritemode_mount}" ;;
      container)      echo "${3:-/$Path}" ;;
    esac
    return 0
  }

  Containerpath="$Path"
  [ "$Createcontaineruser" = "no" ] || {
    [ "$Sharehome" = "host" ] || {
      [ -n "$Containeruserhome" ] && grep -q "^$Containeruserhome" <<< "$Path" && Containerpath="$(sed "s%^$Containeruserhome%/home.host%" <<< "$Containerpath")"
    }
    [ "$Containerpath" = "$Containeruserhosthome" ] && [ "$Persistanthomevolume" != "$Containeruserhosthome" ] && Containerpath="/home.host/$Containeruser"
  }

  # not on Windows
  [ -z "$Winsubsystem" ] && {
    case "$Mode" in
      unix|subsystem) echo "$Path" ;;
      windows)        warning "convertpath(): Nonsense path conversion $Mode: $Path" ; return 1 ;;
      #volume)         echo "'$Path':'${3:-$Path}'$Readwritemode" ;;
      #container)      echo "${3:-$Path}" ;;
      volume)         echo "'$Path':'${3:-$Containerpath}'${Readwritemode}" ;;
      mount)          echo "type=bind,source='$Path',target='${3:-/$Containerpath}'${Readwritemode_mount}" ;;
      container)      echo "${3:-$Containerpath}" ;;
    esac
    return 0
  }

  case "$Winsubsystem" in
    WSL1)
      [ -z "$Drive" ] && case "$Mode" in
        windows|unix|volume)
          debugnote "convertpath(): Request of WSL path: $Path"
          grep -q "$Cachefolder" <<< "$Path" || {
            [ "$Readwritemode" = ":rw" ] && warning "Request of Windows path to path within WSL:
  $Path
  Write access from Windows host to WSL files can damage the WSL file system.
  Read-only access is ok.
  Option --share: You can add :ro to the path to allow read-only access.
  Example: --share='$Path:ro'"
          }
        ;;
      esac
    ;;
  esac

  case "$Drive" in
    "") # Path points into subsystem
      Path="${Path#"$Winsubpath"}"
      Drive="$(cut -c2 <<<"$Winsubpath")"
      case "$Mode" in
        windows)      echo "${Drive^}:$(cut -c3- <<< "$Winsubpath")$Path" ;;
        unix)         echo "$Winsubpath$Path" ;;
        subsystem)    echo "$Path" ;;
        volume)
          case "$Mobyvm" in
            no)  echo "'$Path':'${3:-$Path}'$Readwritemode" ;;
            yes) echo "'$Winsubpath$Path':'${3:-$Path}'$Readwritemode" ;;
          esac
        ;;
        mount)
          case "$Mobyvm" in
            no)  echo "type=bind,source='$Path',target='${3:-$Path}'$Readwritemode_mount" ;;
            yes) echo "type=bind,source='$Winsubpath$Path',target='${3:-$Path}'$Readwritemode_mount" ;;
          esac
        ;;
        container)    echo "${3:-$Path}" ;;
      esac
    ;;
    *) # Path outside of subsystem
      case "$Mode" in
        windows)      echo "${Drive^}:$Path" ;;
        unix)         echo "/$Drive$Path" ;;
        subsystem)    echo "$Winsubmount/$Drive$Path" ;;
        volume)       echo "'/$Drive$Path':'${3:-/$Drive$Path}'$Readwritemode" ;;
        mount)        echo "type=bind,source='/$Drive$Path',target='${3:-/$Drive$Path}'$Readwritemode_mount" ;;
        container)    echo "${3:-/$Drive$Path}" ;;
      esac
    ;;
  esac

  return 0
}
getwslpath() {                  # get path to currently running WSL system

  # Fork from https://github.com/Microsoft/WSL/issues/2578#issuecomment-354010141

  local RUN_ID= BASEPATH=

  RUN_ID="/tmp/$(makecookie)"

  # Mark our filesystem with a temporary file having an unique name.
  touch "${RUN_ID}"

  powershell.exe -Command '(Get-ChildItem HKCU:\Software\Microsoft\Windows\CurrentVersion\Lxss | ForEach-Object {Get-ItemProperty $_.PSPath}).BasePath.replace(":", "").replace("\", "/")' | while IFS= read -r BASEPATH; do
    # Remove trailing whitespaces.
    BASEPATH="${BASEPATH%"${BASEPATH##*[![:space:]]}"}"
    # Build the path on WSL.
    BASEPATH="/mnt/${BASEPATH,}/rootfs"

    # Current WSL instance doesn't have an access to its mount from within
    # itself despite all others are available. That's the hacky way we're
    # using to determine current instance.
    #
    # The second of part of the condition is a fallback for a case if our
    # trick will stop working. For that we've created a temporary file with
    # an unique name and now seeking it among all WLSs.
    if ! ls "${BASEPATH}" > /dev/null 2>&1 || [ -f "${BASEPATH}${RUN_ID}" ]; then
      echo "${BASEPATH}"
      # You can create and simultaneously run multiple WSL instances, comment
      # out the "break", run this script within each one and it'll return only
      # single value.
      break
    fi
  done
  rm "${RUN_ID}"
  return 0
}
get_xpath() {                   # --xc: path to X server
  case "$Xcontainer" in
    yes) echo "/usr/bin/${1:-}" ;;
    no)  command -v "${1:-}" ;;
  esac
}
mkfile() {                      # create file $1 owned by $Hostuser
  :> "${1:-}"                   || return 1
  chown "$Hostuser"    "${1:-}" || return 1
  chgrp "$Hostusergid" "${1:-}" || return 1
  chmod 644            "${1:-}" || return 1
  [ -n "${2:-}" ] && { 
    chmod "${2:-}" "${1:-}"     || return 1
  }
  return 0
}
myrealpath() {                  # real path of possible symlink
  [ -z "$*" ] && return 1
  command -v realpath >/dev/null && {
    realpath "$*"
  } || {
    [ -h "$*" ] && warning "Could not check for symbolic links.
  Please install 'realpath' (package 'coreutils'),
  or provide real file path instead of symbolic link path.
  Possible symbolic link: $*"
    echo "$*"   ### FIXME: Maybe workaround with ls
    return 1
  }
}
waitforlogentry() {             # wait for entry $3 in logfile $2 of application $1
  # $1 is the application we are waiting for to be ready
  # $2 points to logfile
  # $3 keyword to wait for
  # $4 possible error keywords
  # $5 time to wait in seconds or infinity. default: 60

  local Startzeit Uhrzeit Dauer Count=0 Schlaf
  local Errorkeys="${4:-}"
  local Warten="${5:-60}"
  local Error=

  Startzeit="$(date +%s ||:)"
  Startzeit="${Startzeit:-0}"
  [ "$Warten" = "infinity" ] && Warten=32000

  debugnote "waitforlogentry(): ${1:-}: Waiting for logentry \"${3:-}\" in $(basename "${2:-}")"

  while ! grep -q "${3:-}" <"${2:-}" ; do
    Count="$(( Count + 1 ))"
    Uhrzeit="$(date +%s ||:)"
    Uhrzeit="${Uhrzeit:-0}"
    Dauer="$(( Uhrzeit - Startzeit ))"
    Schlaf="$(( Count / 10 ))"
    [ "$Schlaf" = "0" ] && Schlaf="0.5"
    mysleep "$Schlaf"

    [ "$Dauer" -gt "10" ] && debugnote "waitforlogentry(): ${1:-}: Waiting since ${Dauer}s for log entry \"${3:-}\" in $(basename "${2:-}")"

    [ "$Dauer" -gt "$Warten" ] && error "waitforlogentry(): ${1:-}: Timeout waiting for entry \"${3:-}\" in $(basename "${2:-}")
  Last lines of $(basename "${2:-}"):
$(tail "${2:-}")"

#    grep -i -q -E 'xinit: giving up|unable to connect to X server|Connection refused|server error|Only console users are allowed|Failed to process Wayland|failed to create display|] fatal:' <"${2:-}" && \
    [ "$Errorkeys" ] && grep -i -q -E "$Errorkeys" <"${2:-}" && \
      error "waitforlogentry(): ${1:-}: Found error message in logfile.
  Last lines of logfile $(basename "${2:-}"):
$(tail "${2:-}")"

    rocknroll || {
      debugnote "waitforlogentry(): ${1:-}: Stopped waiting for ${3:-} in $(basename "${2:-}") due to terminating signal."
      Error=1
      break
    }
  done
  [ "$Error" ] && return 1

  debugnote "waitforlogentry(): ${1:-}: Found log entry \"${3:-}\" in $(basename "${2:-}")."
  return 0
}
writeaccess() {                 # check if useruid $1 has write access to folder $2
  local dirVals= gMember= IFS=
  IFS=$'\t' read -a dirVals < <(stat -Lc "%U	%G	%A" "${2:-}")
  [ "$(id -u "$dirVals")" == "${1:-}" ] && [ "${dirVals[2]:2:1}" == "w" ]   && return 0
  [ "${dirVals[2]:8:1}" == "w" ]                                          && return 0
  [ "${dirVals[2]:5:1}" == "w" ] && {
    gMember="$(groups "${1:-}" 2>/dev/null)"
    [[ "${gMember[*]:2}" =~ ^(.* |)${dirVals[1]}( .*|)$ ]]                && return 0
  }
  [ "w" = "$(getfacl -pn "${2:-}" | grep "user:${1:-}:" | rev | cut -c2)" ] && return 0 || return 1
}

#### special jobs of x11docker (not running X or docker)
buildimage() {                  # --build: build image from x11docker repository Dockerfile
  # Build image $1 from x11docker repository

  local Wwwpath Buildpath Imagename

  # check image name, should have leading 'x11docker/'
  Imagename="${1:-}"
  grep -q "x11docker" <<< "$Imagename" || Imagename="x11docker/$Imagename"

  # remote and local pathes
  Wwwpath="https://raw.githubusercontent.com/mviereck/dockerfile-$(tr / - <<< "$Imagename")/master"
  Buildpath="${TMPDIR:-/tmp}/x11docker-build-$(unspecialstring "$Imagename")"

  grep -q "dockerfile-x11docker-" <<< "$Wwwpath" || error "Option --build: x11docker only supports building of
  images provided at x11docker repository https://github.com/mviereck"

  mkdir -p "$Buildpath"
  cd "$Buildpath"
  download || error "Option --build: Please install 'curl' or 'wget' to allow a download"

  note "Download of $Wwwpath/Dockerfile"
  download "$Wwwpath/Dockerfile" "$Buildpath/Dockerfile" || error "Option --build: Did not find a Dockerfile for $Imagename
  at $Wwwpath"
  [ "$Imagename" = "x11docker/xserver" ] && {
    note "Download of $Wwwpath/XlibNoSHM.so"
    download "$Wwwpath/XlibNoSHM.so" "$Buildpath/XlibNoSHM.so" || error "Option --build: Downloading XlibNoSHM.so failed."
  }

  note "Building $Imagename"
  $Backendbin build -t "$Imagename" "$Buildpath" || error "Option --build: Building image '$Imagename' failed."

  rm -rf "$Buildpath"
  return 0
}
cleanup() {                     # --cleanup : check for non-removed containers and left cache files
  # Cleans x11docker cache and removes running and stopped x11docker containers.
  # Does not change --home folders.
  local Orphanedcontainers= Orphanedfolders= Line= Containerinspect Containerid

  note "x11docker will check for orphaned containers from earlier sessions
  for current --backend=$Backend
  This can happen if docker was not closed successfully.
  x11docker will look for those containers and will clean up x11docker cache.
  Caution: any currently running x11docker sessions will be terminated, too."

  cd "$Cachebasefolder" || error "Could not cd to cache folder '$Cachebasefolder'."

  grep -q -- "\.cache/x11docker" <<< "$Cachebasefolder" && Orphanedfolders="$(find "$Cachebasefolder" -mindepth 1 -maxdepth 1 -type d | sed "s%$Cachebasefolder/%%" )"
  
  case "$Backend" in
    docker|podman|nerdctl)
      Orphanedcontainers="$($Backendbin ps -a | grep x11docker_X | rev | cut -d' ' -f1 | rev)"
      Orphanedcontainers="$Orphanedcontainers $(find "$Cachebasefolder" -mindepth 2 -maxdepth 2 -type f -name 'container.id' -exec cat {} \;)"
      Orphanedcontainers="$(env IFS='' echo $Orphanedcontainers)"

      # check for double entrys name/id, check for already non-existing containers
      for Line in $Orphanedcontainers; do
        Containerinspect="$($Backendbin inspect "$Line" 2>/dev/null)"
        [ -n "$Containerinspect" ] && {
          Containerid="$(parse_inspect "$Containerinspect" "Id")"
          Orphanedcontainers="$(sed "s%$Line%$Containerid%" <<< "$Orphanedcontainers")"
          :
        } || {
          Orphanedcontainers="$(sed s/$Line// <<< "$Orphanedcontainers")"
        }
      done
      Orphanedcontainers="$(sort <<< "$Orphanedcontainers" | uniq)"
    ;;
  esac

  [ -z "$Orphanedcontainers$Orphanedfolders" ] && {
    note "No orphaned containers or cache files found. good luck!"
  } || {
    note "Found orphaned containers:
$Orphanedcontainers"
    note "Found orphaned folders in $Cachebasefolder:
$Orphanedfolders"

    for Line in $Orphanedfolders ; do
      [ -d "$Cachebasefolder/$Line/share" ] && [ ! -s "$Cachebasefolder/$Line/share/timetosaygoodbye" ] && {
        note "Found possibly active container for cache dir $Line.
  Will summon it to terminate itself."
        echo timetosaygoodbye >> "$Cachebasefolder/$Line/share/timetosaygoodbye"
      }
    done
    [ -n "$Orphanedfolders" ] && sleep 3

    [ -n "$Orphanedcontainers" ] && {
      note "Removing containers with: $Backendbin rm -f $Orphanedcontainers"
      bash -c "$Backendbin rm -f $Orphanedcontainers" 2>&1
    }
    [ -n "$Orphanedfolders" ] && {
      note "Removing cache files with: rm -R -f $Orphanedfolders"
      rm -R -f $Orphanedfolders 2>&1
    }
  }

  Logfile=

  note "Removing remaining files with: rm -Rf -v \"$Cachebasefolder\"/*"
  rm -Rf -v "${Cachebasefolder:-NONSENSE}"/*

  note "Removing cache base folder with: rmdir -v \"$Cachebasefolder\""
  cd
  [ "$(basename "$Cachebasefolder")" = x11docker ] && rmdir -v "$Cachebasefolder"  || warning "Did not succeed in removing cache folder
  '$Cachebasefolder'
  Please run 'x11docker --cleanup' as root."

  case "$Backend" in
    docker)
      $Backendbin info >/dev/null 2>/dev/null || warning "Could not check for $Backend containers.
  Please run 'x11docker --cleanup' as root
  to make sure that no orphaned containers are left."
    ;;
  esac

  note "Cleanup ready."
}
create_launcher() {             # --launcher: create application launcher on desktop
  local Name=

  command -v xdg-desktop-icon >/dev/null || error "Command 'xdg-desktop-icon' not found.
  x11docker needs it to place the new icon on your desktop.
  Please install xdg-utils"

  note "Will create a new application launcher icon on your desktop.
  If you move the new file to:

    $Hostuserhome/.local/share/applications

  it will appear in your applications menu."

  Name="$Codename"
  [ "$Codename" = "xonly" ] && Name="$(echo "$Xserver" | tr -d '-')"
  Name="${Name% }"

  read -re -p "Please choose a name for your application launcher:
" -i "$Name" Name
  [ -z "$Name" ] && return 1 ### FIXME: check for valid file name / invalid chars?

  Parsedoptions_global="${Parsedoptions_global//--launcher/}"
  Parsedoptions_global="${Parsedoptions_global//--starter/}"
  mkfile "$Cachefolder/$Name.desktop"
  {
    echo "#!/usr/bin/xdg-open
[Desktop Entry]
# x11docker desktop file
Type=Application
Name=$Name
Exec=x11docker $Parsedoptions_global
Icon=x11docker
Comment=
Categories=System
Keywords=docker x11docker $(echo "$Name" | tr -c '[:alpha:][:digit:][:blank:]' ' ' )
"
    case "$(command -v x11docker)" in
      "")echo "TryExec=$0 $Parsedoptions_global" ;;
      *) echo "TryExec=x11docker $Parsedoptions_global" ;;
    esac
  } >> "$Cachefolder/$Name.desktop"

  unpriv "xdg-desktop-icon install --novendor '$Cachefolder/$Name.desktop'"
}
installer() {                   # --install, --update, --update-master, --remove: Installer for x11docker
  # --install:
  #   - copies x11docker to /usr/bin
  #   - installs icon in /usr/share/icons
  #   - creates x11docker.desktop file in /usr/share/applications
  # --update:
  #   - download and install latest release from github, regard existing installation location
  # --update-master:
  #   - download and install latest master version from github, regard existing installation location
  # --remove
  #   - remove installed files

  ### FIXME: (--update)
  ### do not require sudo if installed x11docker is owned by user
  ### maybe not install additional files if x11docker is owned by user
  local Key1= Key2= Oldversion= Newversion= Format=
  local Binpath Binowner Bingroup Binpathdefault

  Binpathdefault="/usr/bin"
  # Detect existing installation location
  case "${1:-}" in
    --install)
      Binpath="$Binpathdefault"
    ;;
    --update|--update-master|--remove)
      grep -q x11docker <<< "$0" && {
        Binpath="$(dirname "$0")"
        [ -d "$Binpath/.git" ] && Binpath="/usr/bin"
        Binpath="$(myrealpath "$Binpath")"
        Binowner="$(stat -c '%U' "$Binpath/x11docker")"
        Bingroup="$(stat -c '%G' "$Binpath/x11docker")"
      }
    ;;
  esac
  Binpath="${Binpath:-$Binpathdefault}"
  Binowner="${Binowner:-root}"
  Bingroup="${Bingroup:-root}"
  [ "$Binpath" != "$Binpathdefault" ] && note "Option ${1:-}: Detected custom installation path:
  $Binpath"

  [ "$Startuser" != "root" ] && {
    case "$Winsubsystem" in
      CYGWIN|MSYS2) ;;
      *)
        case "$Binowner" in
          root)
            error "Must run as root to install, update or remove x11docker system wide."
            return 1
          ;;
          *) warning "Option ${1:-}: Not running as root.
  Installing or updating non-essential files in system wide folders might fail." ;;
        esac
      ;;
    esac
  }

  # Preparing
  case "${1:-}" in
    --install)
      [ -f "./x11docker" ]             || {
        error "File x11docker not found in current folder.
  Try 'x11docker --update' instead."
        return 1
      }
      command -v x11docker > /dev/null && {
        warning "x11docker seems to be installed already.
  Will overwrite existing installation.
  Consider to use option '--update' or '--update-master' instead."
      }
    ;;
    --update|--update-master)
      grep -q x11docker <<< "$0" && {
        Oldversion="$($0 --version)"
        note "Current installed version: x11docker $Oldversion"
      } || {
        Oldversion=""
      }

      [ -d /tmp/x11docker-install ] && rm -R /tmp/x11docker-install
      mkdir -p /tmp/x11docker-install && cd /tmp/x11docker-install || {
        error "Could not create or cd to /tmp/x11docker-install."
        return 1
      }
      download || {
        error "Neither wget nor curl found. Need 'wget' or 'curl' for download.
  Please install wget or curl."
        return 1
      }
      command -v unzip >/dev/null && Format="zip"
      command -v tar >/dev/null   && Format="tar.gz"
      [ "$Format" ] || {
        error "Cannot extract archive. Please install 'unzip' or 'tar'."
        return 1
      }

      case "${1:-}" in
        --update-master)
          note "Downloading latest x11docker master version from github."
          download "https://codeload.github.com/mviereck/x11docker/$Format/master" "x11docker-update.$Format"           || {
            error "Failed to download x11docker from github."
            return 1
          }
        ;;
        --update)
          download "https://raw.githubusercontent.com/mviereck/x11docker/master/CHANGELOG.md" "CHANGELOG.md"            || {
            error "Failed to download CHANGELOG.md from github."
            return 1
          }
          Releaseversion="v$(cat CHANGELOG.md | grep "## \[" | grep -v 'Unreleased' | head -n1 | cut -d[ -f2 | cut -d] -f1)"
          note "Downloading latest x11docker release $Releaseversion from github."
          download "https://codeload.github.com/mviereck/x11docker/$Format/$Releaseversion" "x11docker-update.$Format"  || {
            error "Failed to download x11docker from github."
            return 1
          }
        ;;
      esac

      note "Extracting $Format archive."
      case "$Format" in
        zip)    unzip   "x11docker-update.$Format" ;;
        tar.gz) tar xzf "x11docker-update.$Format" ;;
      esac || {
        error "Failed to extract $Format archive."
        return 1
      }
      echo ""
      cd /tmp/x11docker-install/$(ls -l | grep drwx | rev | cut -d' ' -f1 | rev) || {
        error "Could not cd to /tmp/x11docker-update/$(ls -l | grep drwx | rev | cut -d' ' -f1 | rev)"
        return 1
      }
    ;;
  esac

  # Doing
  case "${1:-}" in
    --install|--update|--update-master)
      note "Installing x11docker in $Binpath"
      cp x11docker "$Binpath/"                                                || {
        error "Could not copy x11docker to $Binpath"
        return 1
      }
      chmod 755 "$Binpath/x11docker"                                          || {
        error "Could not set executable bit on x11docker"
        return 1
      }
      chown "$Binowner:$Bingroup" "$Binpath/x11docker"                        || warning "Could not set ownership '$Binowner:$Bingroup' to '$Binpath/x11docker'"

      note "Installing icon for x11docker with xdg-icon-resource"
      xdg-icon-resource install --context apps --novendor --mode system --size 64 "$(pwd)/x11docker.png" x11docker || warning "Could not install icon for x11docker.
  Is 'xdg-icon-resource' (xdg-utils) installed on your system?"
      xdg-icon-resource uninstall --size 72 x11docker ||:  # deprecated icon size, may still be present.

      note "Storing README.md, CHANGELOG.md and LICENSE.txt in
  /usr/share/doc/x11docker"
      mkdir -p /usr/share/doc/x11docker && {
        cp README.md    /usr/share/doc/x11docker/
        cp CHANGELOG.md /usr/share/doc/x11docker/
        cp LICENSE.txt  /usr/share/doc/x11docker/
      } || note "Error while creating /usr/share/doc/x11docker"

      Newversion="$("$Binpath/x11docker" --version)"
      note "Installed x11docker version $Newversion"
    ;;
    --remove)
      note "Removing x11docker from your system."
      cleanup
      [ -x "$Binpath/x11docker" ]     && rm -v "$Binpath/x11docker"
      [ -x "$Binpath/x11docker-gui" ] && rm -v "$Binpath/x11docker-gui"
      [ -e "/usr/share/applications/x11docker.desktop" ] && rm -v /usr/share/applications/x11docker.desktop
      [ -e "/usr/share/doc/x11docker" ]                  && rm -R -v /usr/share/doc/x11docker
      [ -e "/usr/share/icons/x11docker.png" ]            && rm /usr/share/icons/x11docker.png
      xdg-icon-resource uninstall --size 64 x11docker ||:
      xdg-icon-resource uninstall --size 72 x11docker ||:  # deprecated icon size, may still be present.
      note "Will not remove files in your home folder.
  There may be files left in \$HOME/.local/share/x11docker
  The symbolic link \$HOME/x11docker may exist, too.
  The cache folder \$HOME/.cache/x11docker should be removed already."
    ;;
  esac

  # Cleanup
  case "${1:-}" in
    --update|--update-master)
      note "Removing downloaded temporary files."
      cd ~
      rm -R /tmp/x11docker-install
    ;;
  esac

  # Changelog excerpt
  case "${1:-}" in
    --update)
      echo "$Oldversion" | grep -q beta && {
        warning "You are switching from master branch to stable releases.
  To get latest master beta version, use option --update-master instead"
        Key1="\[${Newversion}\]"
        Key2="https:\/\/github.com\/mviereck\/x11docker\/releases"
      } || {
        Key1="\[${Newversion}\]"
        Key2="\[${Oldversion}\]"
        [ "$Newversion" = "$Oldversion" ] && {
          Key2="https:\/\/github.com\/mviereck\/x11docker\/releases"
          note "Version $Newversion was already installed before this update.
  If you want the latest beta version from master branch, use --update-master."
        }
        [ -z "$Oldversion" ] && Key2="https:\/\/github.com\/mviereck\/x11docker\/releases"
      }
    ;;
    --update-master)
      echo "$Oldversion" | grep -q beta && {
        Key1="\[Unreleased\]"
        Key2="https:\/\/github.com\/mviereck\/x11docker\/releases"
      } || {
        Key1="\[Unreleased\]"
        Key2="\[${Oldversion}\]"
        [ -z "$Oldversion" ] && Key2="https:\/\/github.com\/mviereck\/x11docker\/releases"
      }
    ;;
  esac
  case "${1:-}" in
    --update|--update-master)
      note "Excerpt of x11docker changelog:
$(sed -n "/${Key1}/,/${Key2}/p" /usr/share/doc/x11docker/CHANGELOG.md | head -n-1)"
    ;;
  esac
  note "Ready."
}
set_password() {                # option --password: set container user password
  local Password

  Password="${1:-}"

  command -v perl >/dev/null || {
    error "Option --password: command 'perl' not found.
  perl is needed to generate an encrypted password."
    return 1
  }
  [ "$Password" = "INTERACTIVE" ] && {
    read -rs -p "Please type in a new container user password (chars are invisible): " Password
    echo ""
    [ -z "$Password" ] && {
      error "Empty input, password not changed."
      return 1
    }
  }
  Password="$(perl -e "print crypt(\"$Password\", \"salt\")")"
  mkdir -p "$(dirname "$Passwordfile")"
  echo "$Containeruserpassword" > "$Passwordfile"
  chmod 600                       "$Passwordfile"
  note "Option --password: Password changed, exiting."
  return 0
}

#### host integration features
check_windowmanager() {         # option --wm: search a host window manager
  # check --wm arguments, adjust mode
  case "$Xcontainer" in
    yes)
      case "$Windowmanagermode" in
        auto|host)
          Windowmanagermode="host"
          Windowmanagercommand="openbox --sm-disable" ;;
        *) Windowmanagermode="none" ;;
      esac
      return 0
    ;;
  esac

  case "$Windowmanagermode" in
    ""|none)
      Windowmanagermode="none"
      return 0
    ;;
    auto)
      Windowmanagermode="host"
      [ -n "$Windowmanagercommand" ] && {
        command -v "$(cut -d' ' -f1 <<< "$Windowmanagercommand")" >/dev/null && {
          Windowmanagermode="host"
        } || {
          note "Option --wm: Did not find command on host: $Windowmanagercommand"
          check_fallback
        }
      }
    ;;
    host) ;;
  esac

  # Find a host window manager
  [ "$Windowmanagercommand" ] || for Windowmanagercommand in $Wm_all WM_NOT_FOUND; do
    command -v "$Windowmanagercommand" >/dev/null && break
  done

  [ "$Windowmanagercommand" = "WM_NOT_FOUND" ] && {
    Windowmanagercommand=""
    note "Option --wm: No host window manager found.
    Please install a supported one. Recommended:
  $Wm_recommended_nodesktop_light
  Fallback: Setting --wm=none"
    check_fallback
    Windowmanagermode="none"
  }

  [ "$Windowmanagermode" = "none" ] && return 0

  [ "$Xtest" = "yes" ] && warning "Options --xtest --wm: Did not disable X extension XTEST
  for X server $Xserver.
  If your host window manager '$Windowmanagercommand' can start applications
  on its own (for example with a context menu), container applications
  can abuse this to run and remotely control host applications.
  If you provide content of X server $Xserver over network to others,
  they may take control over your host system!"

  # command adjustment for some host window managers
  case "$(basename "$(cut -d' ' -f1 <<< "$Windowmanagercommand")")" in
    cinnamon|cinnamon-session) Windowmanagercommand="cinnamon --sm-disable";;
    compiz) # if none, create minimal config to have usable window decoration and can move windows
      [ -e "$Hostuserhome/.config/compiz-1/compizconfig/Default.ini" ] || {
        unpriv "mkdir -p '$Hostuserhome/.config/compiz-1/compizconfig'"
        mkfile "$Hostuserhome/.config/compiz-1/compizconfig/Default.ini"
        echo '[core]
s0_active_plugins = core;composite;opengl;decor;resize;move;
' >> "$Hostuserhome/.config/compiz-1/compizconfig/Default.ini"
      }  ;;
    enlightenment|e17|e16|e19|e20|e) Windowmanagercommand="enlightenment_start" ;;
    matchbox) Windowmanagercommand="matchbox-window-manager"  ;;
    mate|mate-session) Windowmanagercommand="mate-session -f" ;;
    mate-wm) Windowmanagercommand="marco --sm-disable"  ;;
    openbox)
      Windowmanagercommand="openbox --sm-disable"
      [ -e "/etc/xdg/openbox/rc.xml" ] && {
        cp /etc/xdg/openbox/rc.xml "$Sharefolder/openbox-nomenu.rc"
        sed -i /ShowMenu/d         "$Sharefolder/openbox-nomenu.rc"
        sed -i s/NLIMC/NLMC/       "$Sharefolder/openbox-nomenu.rc"
        Windowmanagercommand="$Windowmanagercommand --config-file '$Sharefolder/openbox-nomenu.rc'"
      }
    ;;
  esac

  case "$Windowmanagermode" in
    host)
      command -v "wmctrl" >/dev/null || note "Option --wm: Command 'wmctrl' not found.
  It is recommended to install wmctrl to set only one virtual desktop
  on window manager ${Windowmanagercommand}.
  $Wikipackagesimage"
    ;;
  esac

  verbose "Detected host window manager: ${Windowmanagercommand:-"(none)"}"
  return 0
}
clean_xhost() {                 # option --clean-xhost: disable xhost policies on host X
  [ -z "$Hostdisplay" ] && note "Option --clean-xhost: No host X display found." && return 1
  [ -z "$Hostxauthority" ] && warning "Option --clean-xhost: You host X server does not provide
  an authentication cookie in \$XAUTHORITY.
  Host applications started after xhost cleanup might fail to start."
  echo "Option --clean-xhost:" >> "$Xinitlogfile"
  DISPLAY="$Hostdisplay" XAUTHORITY="$Hostxauthority" disable_xhost >> "$Xinitlogfile" 2>&1
}
setup_clipboard() {             # option --clipboard: create shareclipboard script
  # Clipboard sharing works different depending on the new X server
  #  - xpra and nxagent have their own clipboard management.
  #  - Only xpra supports image clips.
  #  - --hostdisplay accesses the clipboard from host X directly
  #  - Other X servers: A script is created to synchronize clipboard between X servers.
  #  - No clipboard support for Wayland yet.
  # The script uses xclip or xsel. It is executed in xinitrc.

  local Clipsend= Clipreceive=

  case "$Xserver" in
    --tty|--weston|--hostwayland|--kwin)
      note "Option --clipboard is not supported for $Xserver.
  Fallback: Disabling option --clipboard."
      check_fallback
      Shareclipboard="no"
      return 1
    ;;
    --nxagent|--xpra*|--xwin) ;; # have their own clipboard management, look at create_xcommand().

    --xephyr|--xorg|--xvfb|--xwayland|--weston-xwayland)
      # check for either xclip or xsel
      command -v xsel >/dev/null && {
        Clipsend="xsel --clipboard --input"
        Clipreceive="xsel --clipboard --output"
      }
      [ "$Xcontainer" = "yes" ] || command -v xclip >/dev/null && {
        Clipsend="xclip -selection clipboard -in"
        Clipreceive="xclip -selection clipboard -out"
      }
      [ -z "$Clipsend" ] && {
        note "Option --clipboard: Need either xclip or xsel
  for clipboard sharing with X server $Xserver.
  Fallback: Disabling option --clipboard."
        check_fallback
        Shareclipboard="no"
      }
      [ -z "$Hostdisplay" ] && {
        note "Option --clipboard: No host display DISPLAY found
  to share clipboard. Fallback: Disabling option --clipboard"
        check_fallback
        Shareclipboard="no"
      }

      echo "#! /usr/bin/env bash
# share clipboard between X servers $Hostdisplay and $Newdisplay

$(declare -f mysleep)
$(declare -f rocknroll)
Timetosaygoodbyefile='$Timetosaygoodbyefile'

while rocknroll ; do
  # read content of clipboard of first X server $Hostdisplay
  X1clip=\"\$(env DISPLAY=$Hostdisplay XAUTHORITY=$Hostxauthority $Clipreceive)\"

  # check if clipboard of first X server has changed; if yes, send new content to second X server
  [ \"\$Shareclip\" != \"\$X1clip\" ] && {
    Shareclip=\"\$X1clip\"
    env DISPLAY=$Newdisplay XAUTHORITY=$Xclientcookie $Clipsend <<<  \"\$Shareclip\"
#    echo \"\$Shareclip\" | env DISPLAY=$Newdisplay XAUTHORITY=$Xclientcookie $Clipsend
  }
  Shareclip=\"\${Shareclip:-' '}\"     # avoid empty string error
  mysleep 0.3                       # sleep a bit to avoid high cpu usage

  # read content of clipboard of second X server $Newdisplay
  X2clip=\"\$(env DISPLAY=$Newdisplay XAUTHORITY=$Xclientcookie $Clipreceive)\"

  # check if clipboard of second X server has changed; if yes, send new content to first X server
  [ \"\$Shareclip\" != \"\$X2clip\" ] && {
    Shareclip=\"\$X2clip\"
    env DISPLAY=$Hostdisplay XAUTHORITY=$Hostxauthority $Clipsend <<<  \"\$Shareclip\"
#    echo \"\$Shareclip\" | env DISPLAY=$Hostdisplay XAUTHORITY=$Hostxauthority $Clipsend
  }
  Shareclip=\"\${Shareclip:-' '}\"     # avoid empty string error
  mysleep 0.3                       # sleep a bit to avoid high cpu usage
done
" >> "$Clipboardrc"
cat $Clipboardrc
    ;;
  esac
  return 0
}
setup_gpu_devicelist() {        # option --gpu: print list of GPU devices
  find /dev/dri /dev/nvidia* /dev/vga_arbiter /dev/nvhost* /dev/nvmap -maxdepth 0 2>/dev/null ||:
}
setup_gpu() {                   # option --gpu: share /dev/dri and check nvidia driver
  # Easiest case: share /dev/dri.
  # Works for open source MESA drivers on host and in image.
  # Debian packages for MESA drivers in image: libgl1-mesa-dri, libglx-mesa0
  #
  # Closed source NVIDIA drivers does not integrate well within linux.
  # Instead, free nouveau driver is a better choice, or no NVIDIA hardware at all.
  # Possibilities:
  # - Install NVIDIA driver in image. It must be the very same version as on your host.
  #   The image is not portable anymore.
  # - x11docker can install NVIDIA driver on the fly in running container. See notes below.
  #
  # g  $Nvidiainstallerfile   nvidia driver file to install in container in containerrootrc
  # g  $Nvidiaversion  nvidia driver version on host

  local Gpudevice

  # --xoverip
  case "$Xoverip" in
    yes)
      note "Option --gpu with X over IP: Enabling indirect rendering with --gpu=iglx."
      Iglx="yes"
      Sharegpu="no"
      case "$Xserver" in
        --xorg) ;;
        *) note "Option --gpu=iglx is known to accelerate with option --xorg only." ;;
      esac
      return 0
    ;;
  esac

  warning "Option --gpu degrades container isolation.
  Container gains access to GPU hardware.
  This allows reading host window content (palinopsia leak)
  and GPU rootkits (compare proof of concept: jellyfish).
  An alternative is --gpu=iglx that works with --xorg only." ### FIXME warning for virgl, too

  Containerusergroups="$Containerusergroups video render"

  # check device files
  while read -r Gpudevice ; do
    store_runoption volume "$Gpudevice"
  done < <(setup_gpu_devicelist)

  [ -z "$Nvidiaversion" ] && return 0

  # support for prime-run / discrete NVIDIA card #394
  [ -n "$__NV_PRIME_RENDER_OFFLOAD" ] && store_runoption env "__NV_PRIME_RENDER_OFFLOAD=$__NV_PRIME_RENDER_OFFLOAD"
  [ -n "$__VK_LAYER_NV_optimus" ]     && store_runoption env "__VK_LAYER_NV_optimus=$__VK_LAYER_NV_optimus"
  [ -n "$__GLX_VENDOR_LIBRARY_NAME" ] && store_runoption env "__GLX_VENDOR_LIBRARY_NAME=$__GLX_VENDOR_LIBRARY_NAME"

  # check for closed source nvidia driver on host, provide automated installation, warn about disadvantages
  debugnote "NVIDIA: Detected driver version $Nvidiaversion on host."

  [ "$Runtime" = "nvidia" ] && {
    debugnote "NVIDIA: Option --runtime=nvidia: Skipping driver installation."
    Nvidiainstallerfile=""
    return 0
  }

  Nvidiainstallerfile="$(find /usr/local/share/x11docker/NVIDIA*$Nvidiaversion*.run $Localsharedir/NVIDIA*$Nvidiaversion*.run 2>/dev/null | tail -n1 )"
  Nvidiainstallerfile="$(myrealpath "$Nvidiainstallerfile" 2>/dev/null)"

  [ -e "$Nvidiainstallerfile" ] && {
    debugnote "NVIDIA: Found proprietary closed source NVIDIA driver installer
  $Nvidiainstallerfile"
    [ "$Containersetup" = "no" ] && {
      note "Options --no-setup --gpu: Cannot install NVIDIA driver
  with option --no-setup. Fallback: Disabling option --gpu"
      Sharegpu="no"
      return 1
    }
    [ "$Capdropall" = "yes" ] && warning "Option --gpu: Installing NVIDIA driver in container
  requires container privileges that x11docker would drop otherwise.
  Though, they are still within default docker capabilities."
    Allownewprivileges="yes"
    store_runoption cap CHOWN
    store_runoption cap FOWNER
    return 0
  }

  Nvidiainstallerfile=""

  note "Option --gpu: You are using the closed source NVIDIA driver.
  GPU acceleration will only work if you have installed the very same driver
  version in image. That makes images less portable.
  It is recommended to use free open source nouveau driver on host instead.
  Ask NVIDIA corporation to at least publish their closed source API,
  or even better to actively support open source driver nouveau.
  
  To use the GPU without NVIDIA driver in container, you can use 
  less performant options --gpu=iglx (--xorg only) or --gpu=virgl (needs --xc)."

  note "Option --gpu: x11docker can try to automatically install NVIDIA driver
  version $Nvidiaversion in container on every container startup.
  Drawbacks: Container startup is a bit slower and its security will be reduced.

  You can look here for a driver installer file:
    https://www.nvidia.com/Download/index.aspx
    https://http.download.nvidia.com/
  A direct download URL is probably:
    https://http.download.nvidia.com/XFree86/Linux-x86_64/$Nvidiaversion/NVIDIA-Linux-x86_64-$Nvidiaversion.run
  If you got a driver, store it at one of the following locations:
    $Localsharedir/
    /usr/local/share/x11docker/

  Be aware that the version number must match exactly the version on host.
  The file name must begin with 'NVIDIA', contain the version number $Nvidiaversion
  and end with suffix '.run'."

  return 0
}
setup_hostdbus() {              # option --hostdbus: connect to host DBus session daemon.
  warning "--hostdbus: Connecting container to host DBus degrades
  container isolation. Container applications might send malicious requests."
  Dbusrunsession=no

  [ "$DBUS_SESSION_BUS_ADDRESS" ] || {
    # no running DBus session?
    command -v dbus-launch >/dev/null && {
      note "Option --hostdbus: DBUS_SESSION_BUS_ADDRESS is empty.
  Creating abstract DBus socket with dbus-launch."
      export $(dbus-launch)
    } || {
      note "Option --hostdbus: Is DBus running on host?
  Did not find an active session and did not find dbus-launch.
  DBUS_SESSION_BUS_ADDRESS is empty.
  $Wikipackages"
      check_fallback
      return 1
    }
  }

  grep -q "unix:path" <<< "$DBUS_SESSION_BUS_ADDRESS" && {
    # DBus socket file
    case "$Runtime" in
      kata-runtime)
        note "Option --hostdbus with a unix socket is not possible
  with --runtime=$Runtime."
        check_fallback
        return 1
      ;;
      *)
        store_runoption env "DBUS_SESSION_BUS_ADDRESS=$DBUS_SESSION_BUS_ADDRESS"
        store_runoption volume "/$(cut -d/ -f2- <<<"$DBUS_SESSION_BUS_ADDRESS")"
      ;;
    esac
  }

  grep -q "unix:abstract" <<< "$DBUS_SESSION_BUS_ADDRESS" && {
    # DBus abstract socket (dbus-launch)
    store_runoption env "DBUS_SESSION_BUS_ADDRESS=$DBUS_SESSION_BUS_ADDRESS"
    [ "${DBUS_SESSION_BUS_PID:-}" ]      && store_runoption env  "${DBUS_SESSION_BUS_PID:-}"
    [ "${DBUS_SESSION_BUS_WINDOWID:-}" ] && store_runoption env  "${DBUS_SESSION_BUS_WINDOWID:-}"
    [ "$Network" != "host" ] && {
      note "Option --hostdbus: Did not find a DBus session socket file
  but an abstract unix socket. To allow access for container,
  x11docker needs insecure option '--network=host'.
  Sharing host dbus will fail."
      check_fallback
      return 1
    }
  }
  return 0
}
setup_printer() {               # option --printer: connect to cups printer server
  # Default CUPS setups create a unix socket /run/cups/cups.sock as given from 'lpstat -H'.
  # Sharing this socket and pointing environment variable CUPS_SERVER to it serves most cases.
  # Possible CUPS network setups need to allow access from container, see note below.
  local Cupsserver=

  command -v lpstat >/dev/null || {
    note "Option --printer: command lpstat not found.
  Is cups printer server installed on your system?
  Error: Cannot share access to printer."
    Sharecupsmode=""
    return 1
  }

  case "$Sharecupsmode" in
    auto)
      Sharecupsmode="socket"
      [ "$Snapsupport" = "yes" ]      && Sharecupsmode="tcp"
      [ "$Runtime" = "kata-runtime" ] && Sharecupsmode="tcp"
      #[ "$Runtime" = "sysbox-runc" ]  && Sharecupsmode="tcp"
    ;;
  esac

  case "$Sharecupsmode" in
    tcp)
      Cupsserver="$Hostip:631"
      [ "$Network" = "none" ] && {
        note "Option --printer=tcp does not work without network access.
  Consider to enable option -I, --network."
        check_fallback
        return 1
      }
    ;;
    socket) Cupsserver="$(lpstat -H)" ;;
  esac

  grep -q ":" <<< "$Cupsserver" && {
    [ "$(cut -d: -f1 <<< "$Cupsserver")" = "localhost" ] && Cupsserver="$Hostip:$(cut -d: -f2 <<< "$Cupsserver")"
    [ "$(cut -d: -f1 <<< "$Cupsserver")" = "127.0.0.1" ] && Cupsserver="$Hostip:$(cut -d: -f2 <<< "$Cupsserver")"
    note "Option --printer: Network setup for CUPS detected.
  Server address: $Cupsserver
  You may need to allow container access in /etc/cups/cupsd.conf, e.g.:

Port 631
<Location />
  # Allow remote access...
  Order allow,deny
  Allow 172.17.0.*
  Allow 172.20.0.*
  Allow 127.0.0.1
</Location>

Also you need to allow network access for each printer in a CUPS GUI."
  }

  [ "$Cupsserver" ]    && store_runoption env "CUPS_SERVER=$Cupsserver"
  [ -e "$Cupsserver" ] && {
    [ "$(dirname "$Cupsserver")" = "/run/cups" ] && store_runoption volume "/run/cups" || store_runoption volume "$Cupsserver"
  }

  return 0
}
setup_sound_alsa() {            # option --alsa: share sound devices
  # Sound with ALSA is directly supported by the kernel and only needs to share devices in /dev/snd.
  # libasound2 in image is recommended.
  # The desired sound card can be specified with environment variable ALSA_CARD. See card name in 'aplay -l'.
  # Further documentation at https://github.com/mviereck/x11docker/wiki/Container-sound:-ALSA-or-Pulseaudio

  warning "Option --alsa: ALSA sound degrades container isolation.
  Sharing device files in /dev/snd, container gains access to sound hardware.
  Container applications can catch audio output and microphone input."

  [ "$Alsacard" ] && store_runoption env "ALSA_CARD=$Alsacard"

  pgrep pulseaudio >/dev/null && note "Option --alsa: It seems that pulseaudio is running
  on your host. Pulseaudio can interfere with ALSA sound.
  Host sound may not work while container is playing sound and vice versa.
  Alternative: with pulseaudio on host and in image, use option --pulseaudio."

  [ -d /dev/snd ] && store_runoption volume "/dev/snd" || {
    warning "Option --alsa: /dev/snd not found.
  Sound support not possible."
    check_fallback
    Sharealsa="no"
    return 1
  }

  Containerusergroups="$Containerusergroups audio"

  return 0
}
setup_sound_pulseaudio() {      # option --pulseaudio: set up pulseaudio connection
  # Allowing container access to Pulseaudio on host can be done with a shared socket or over TCP.
  # Sharing host user socket in XDG_RUNTIME_DIR fails since Pulseaudio v12.0.
  # Instead, a new socket is created with pactl.
  # TCP module is created after container startup to authenticate it with container IP.
  # Detailed documentation at: https://github.com/mviereck/x11docker/wiki/Container-sound:-ALSA-or-Pulseaudio
  #
  # g  $Pulseaudiomode    =tcp or =socket: Connect over tcp or with shared socket
  # g  $Pulseaudioport    TCP port
  local Lowerport= Upperport=

  command -v pactl >/dev/null || {
    note "Option --pulseaudio: pactl not found.
  Is pulseaudio installed and running on your host system?
  Fallback: Disabling option --pulseaudio.
  You can try option --alsa instead."
    check_fallback
    Pulseaudiomode=""
    return 1
  }

  [ -z "$Pulseaudiomode" ] && Pulseaudiomode="socket"
  [ "$Pulseaudiomode" = "auto" ] && {
    Pulseaudiomode="socket"
    [ "$Containeruser" = "$Hostuser" ]                        || Pulseaudiomode="tcp"
    [ "$Runtime" = "kata-runtime" ]                           && Pulseaudiomode="tcp"
    [ "$Runtime" = "sysbox-runc" ]                            && Pulseaudiomode="tcp"
    [ "$Snapsupport" = "yes" ]                                && Pulseaudiomode="tcp"
    unpriv 'LC_ALL=C pactl info | grep -q "User Name: pulse"' && Pulseaudiomode="tcp"
    [ "$Pulseaudiomode" = "tcp" ] && note "Option --pulseaudio: Enabling TCP mode"
  }

  [ "$Pulseaudiomode" = "tcp" ] && [ "$Network" = "none" ] && {
    note "Option --pulseaudio: Without option -I, --network
  pulseaudio sound over TCP is not possible but needed in your setup."
    Pulseaudiomode=""
    check_fallback
    return 1
  }

  warning "Option --pulseaudio allows container applications
  to catch your audio output and microphone input."

  case "$Pulseaudiomode" in
    socket|tcp)
      grep -q -i "PipeWire" <<< "$(unpriv "pactl info")" && note "Option --pulseaudio: Found pipewire sound server.
  If pulseaudio sound fails, you can try --pulseaudio=host instead."
    ;;
  esac
  
  case "$Pulseaudiomode" in
    socket)
      # create pulseaudio socket
      Pulseaudiomoduleid="$(unpriv "pactl load-module module-native-protocol-unix socket='$Pulseaudiosocket' 2>&1")"

      [ "$Pulseaudiomoduleid" ] && {
        storeinfo "pulseaudiomoduleid=$Pulseaudiomoduleid"
        store_runoption env "PULSE_SERVER=unix:$(convertpath share "$Pulseaudiosocket")"
        store_runoption env "PULSE_COOKIE=$(convertpath share "$Pulseaudiocookie")"
      } || {
        note "Option --pulseaudio: command pactl failed.
  Is pulseaudio running at all on your host?
  Fallback: Disabling option --pulseaudio.
  You can try option --alsa instead."
        check_fallback
        Pulseaudiomode=""
        return 1
      }

      echo "# Connect to host pulseaudio server using mounted UNIX socket
default-server = unix:$(convertpath share "$Pulseaudiosocket")
# Prevent a server running in container
autospawn = no
daemon-binary = /bin/true
# Prevent use of shared memory
enable-shm = false
" >> "$Pulseaudioconf"
        verbose "Generated pulseaudio client.conf:
$(nl -ba < "$Pulseaudioconf")"
    ;;
    tcp)
      read Lowerport Upperport < /proc/sys/net/ipv4/ip_local_port_range 2>/dev/null
      [ "$Lowerport" ] || Lowerport=33000
      [ "$Upperport" ] || Upperport=60000
      while : ; do
        Pulseaudioport="$(shuf -i $Lowerport-$Upperport -n1)"
        ss -lpn | grep -q ":$Pulseaudioport " || break
      done
      [ -e "$Hostuserhome/.config/pulse/cookie" ] && cp "$Hostuserhome/.config/pulse/cookie" "$Pulseaudiocookie" || note "Option --pulseaudio: Did not find cookie
  $Hostuserhome/.config/pulse/cookie"
      store_runoption env "PULSE_SERVER=tcp:$Hostip:$Pulseaudioport"
    ;;
    host)
      Pulseaudiosocket="$(unpriv "env LC_ALL=C pactl info" | grep 'Server String' |rev|cut -d' ' -f1|rev)"
      store_runoption env "PULSE_SERVER=unix:/tmp/pulseaudio.socket.host"
#      store_runoption env "PULSE_COOKIE=$(convertpath share "$Pulseaudiocookie")"
    ;;
  esac
  return 0
}
setup_webcam() {                # option --webcam: share webcam devices
  # Webcam devices appear as /dev/video* files.
  # Unprivileged users need to be in group video.
  # (This works only if webcam is plugged in before container starts.
  # Hotplug support would have to be different.)
  local Webcamdevice

  [ "$Sharewebcam" = "yes" ] && warning "Option --webcam: Container applications might look
  at you and also might take screenshots of your Desktop."

  while read -r Webcamdevice ; do
    store_runoption volume "$Webcamdevice"
  done < <(find /dev/video* -maxdepth 0 2>/dev/null || note "Option --webcam: No webcam devices /dev/video* found.
  Webcam in container will fail.")
  Containerusergroups="$Containerusergroups video"

  # at least cheese and gnome-ring need some device information from udev.
  store_runoption volume "/run/udev/data:ro"
  return 0
}

#### X server setup
check_newxenv()  {              # find free display
  local Line
  # find free display number
  [ "$Newdisplaynumber" ] || {
    Newdisplaynumber="100"
    while :; do
      case "$Xserver" in
        --xwin|--runx) Newdisplaynumber="$((RANDOM / 10 + 200))" ;;
        *)             Newdisplaynumber="$((Newdisplaynumber + 1))" ;;
      esac
      grep -q -x "$Newdisplaynumber" < "$Numbersinusefile" || [ -n "$(find "/tmp/.X11-unix/X$Newdisplaynumber" "/tmp/.X$Newdisplaynumber-lock" "$XDG_RUNTIME_DIR/wayland-$Newdisplaynumber" 2>/dev/null)" ] || break
    done
  }
  echo "$Newdisplaynumber" >> "$Numbersinusefile"

  # --xoverip
  [ "$Xoverip" ] || {
    case "$Mobyvm" in
      yes)
        Xoverip="yes"
      ;;
    esac
    case "$Xserver" in
      --xwin|--runx)
        Xoverip="yes"
        [ "$Network" = "none" ] && {
          note "Option $Xserver needs network access.
  Enabling option -I, --network."
          check_fallback
          Network=""
        }
      ;;
    esac
    case "$Backend" in
      chroot)
        Xoverip="yes"
      ;;
    esac
  }
  case "$Xoverip" in
    yes)
      case "$Backend" in
        docker|podman|nerdctl)
          [ "$Network" = "none" ] && error "Option --xoverip needs network access.
  Please enable option -I, --network."
        ;;
      esac
    ;;
  esac

  # set $Newdisplay (DISPLAY of container) and $Newxsocket
  case "$Xserver" in
    --hostdisplay)
      case "$Xoverip" in
        yes)
          [ "$(cut -c1 <<< "$Hostdisplay")" = ":" ] && Newdisplay="${Hostip}${Hostdisplay}" || Newdisplay="$Hostdisplay"  ;;
        no|"")
          Newdisplay="$Hostdisplay"
          Newdisplaynumber="$(echo "$Newdisplay" | cut -d: -f2 | cut -d. -f1)"
          Newxsocket="$Hostxsocket"
        ;;
      esac
    ;;
    --weston|--kwin|--hostwayland|--tty)
      Newdisplay=""
      Newxsocket=""
      Xclientcookie=""
      Xservercookie=""
    ;;
    *)
      case "$Xoverip" in
        yes)
          case "$Xcontainer" in
            yes) Newdisplay="XCONTAINERIP:$Newdisplaynumber" ;;
            no)  Newdisplay="$Hostip:$Newdisplaynumber" ;;
          esac
        ;;
        no|"")
          Newdisplay=":$Newdisplaynumber"
          Newxsocket="/tmp/.X11-unix/X$Newdisplaynumber"
          Newxlock="/tmp/.X$Newdisplaynumber-lock"
          [ -n "$(find "$Newxsocket" "$Newxlock" 2>/dev/null)" ] && error "Display $Newdisplay is already in use."
        ;;
      esac
    ;;
  esac

  # set $Newwaylandsocket
  case "$Xserver" in
    --weston*|--kwin*|--xpra-xwayland|--xpra2-xwayland) Newwaylandsocket="wayland-$Newdisplaynumber" ;;
    --hostwayland|--xwayland)                           Newwaylandsocket="$Hostwaylandsocket" ;;
  esac

  return 0
}
check_screensize() {            # check physical and virtual screen size (also option --size)
  local Line=

  [ -z "$Hostdisplay" ] && [ -n "$Hostwaylandsocket" ] && verbose "check_screensize(): Skipping check on pure Wayland environment"

  # check whole display size, can include multiple monitors
  [ -n "$Hostdisplay" ] && {
    command -v xrandr >/dev/null && {
      Line="$(xrandr 2>/dev/null | grep current | head -n1 | cut -d, -f2)"
      Maxxaxis="$(echo "$Line" | cut -d' ' -f3)"
      Maxyaxis="$(echo "$Line" | cut -d' ' -f5)"
      [ -z "$Maxxaxis" ] && {
        Maxxaxis="$(xrandr 2>/dev/null | grep ' connected' | head -n1 | cut -dx -f1 | rev | cut -d' ' -f1 | rev)"
        Maxyaxis="$(xrandr 2>/dev/null | grep ' connected' | head -n1 | cut -dx -f2 | cut -d' ' -f1 | cut -d+ -f1)"
      }
    }
    [ -z "$Maxxaxis" ] && note "check_screensize(): Could not determine your screen size.
  Please improve this by installing 'xrandr' or use option --size=XxY.
  $Wikipackages"
  }

  [ -n "$Screensize" ] && {
    Xaxis="${Screensize%x*}"
    Yaxis="${Screensize#*x}"
  }

  [ -z "$Maxxaxis" ] && {
    Maxxaxis="1920"
    Maxyaxis="1200"
    [ "$Maxxaxis" -lt "${Xaxis:-0}" ] && Maxxaxis="$Xaxis"
    [ "$Maxyaxis" -lt "${Yaxis:-0}" ] && Maxyaxis="$Yaxis"
  }

  # size for windowed desktops, roughly maximized relative to primary monitor
  case "$Xserver" in
    --xpra*) [ "$Desktopmode" = "yes" ] && Xserver="${Xserver}-desktop" ;;
  esac
  [ -z "$Screensize" ] && [ "$Runsonconsole" = "no" ] && case "$Xserver" in
    --xephyr|--weston-xwayland|--weston|--kwin|--kwin-xwayland|--nxagent|--xpra*-desktop)
      Xaxis="$((Maxxaxis-96))"
      Yaxis="$((Maxyaxis-96))"
      Xaxis="$(( $(( $Xaxis / 8 )) * 8 ))"  # avoid grey edge in Xwayland, needs full byte x width
    ;;
  esac
  Xserver="${Xserver%-desktop}"

  [ "$Fullscreen" = "yes" ] && [ "$Runsonconsole" = "no" ] && Screensize="${Maxxaxis}x${Maxyaxis}"

  [ -z "$Xaxis" ] && {
    Xaxis="$Maxxaxis"
    Yaxis="$Maxyaxis"
  }

  # regard scaling (option --scale)
  [ "$Scaling" ] && {
    Xaxis="$(awk -v a="$Xaxis" -v b="$Scaling" 'BEGIN {print (a / b)}')"
    Xaxis="${Xaxis%.*}"
    Yaxis="$(awk -v a="$Yaxis" -v b="$Scaling" 'BEGIN {print (a / b)}')"
    Yaxis="${Yaxis%.*}"
  }

  [ -z "$Screensize" ] && case "$Xserver" in
    --xorg) ;;  # Xorg autodetects screen size, preset only with option --size
    --weston-xwayland|--weston|--kwin|--kwin-xwayland)
      [ "$Runsonconsole" = "no" ] && Screensize="${Xaxis}x${Yaxis}"
    ;;
    *) Screensize="${Xaxis}x${Yaxis}" ;;
  esac

  command -v cvt >/dev/null && Modeline="$(cvt "$Xaxis" "$Yaxis" | tail -n1 | cut -d' ' -f2-)"
  Modeline="$(echo $Modeline | cut -d_ -f1)\" $(echo $Modeline | cut -d_ -f2- | cut -d' ' -f2-)"

  verbose "Virtual screen size: $Screensize"
  verbose "Physical screen size:
  $(xrandr 2>/dev/null | grep Screen ||:)"

  # create set of Modelines if needed
  case "$Xserver" in
    --xpra|--xpra2|--xvfb) Modelinefile="$(create_modelinefile "${Maxxaxis}x${Maxyaxis}")" ;;
  esac

  return 0
}
check_vt() {                    # option --xorg: find free vt / tty
  local Line= Ttyinuse=

  # if started from console, use current tty
  case "$Newxvt" in
    "")
      tty -s && [ "$Runsonconsole" = "yes" ] && {
        Newxvt="$(tty | rev | cut -d/ -f1 | rev)"
        Newxvt="${Newxvt#tty}"
      }
    ;;
    auto) Newxvt="" ;;
    *)    return 0 ;;
  esac

  # check ttys currently in use
  [ "$Newxvt" ] || {
    for Line in $(find /sys/class/vc/vcsa*); do
      Ttyinuse="$Ttyinuse ${Line#/sys/class/vc/vcsa} "
    done
    debugnote "check_vt(): TTYs currently known to kernel: $Ttyinuse"
  }

  [ "$Newxvt" ] && grep -q " $Newxvt " <<< "$Ttyinuse" && warning "TTY $Newxvt seems to be already in use."

  # try to find free tty within range of 8..12
  [ "$Newxvt" ] || {
    for ((Newxvt=8 ; Newxvt<=12 ; Newxvt++)) ; do
      grep -q " $Newxvt " <<< "$Ttyinuse" || break
    done
  }

  # try to find free tty within range of 1..7
  [ "$Newxvt" ] || {
    for ((Newxvt=1 ; Newxvt<=7 ; Newxvt++)) ; do
      grep -q " $Newxvt " <<< "$Ttyinuse" || break
    done
  }

  # try to find free tty with fgconsole. Fails in some cases within X.
  [ "$Newxvt" ] || Newxvt="$(fgconsole --next-available 2>/dev/null)"
  [ "$Newxvt" ] || Newxvt="$(fgconsole --next-available 2>/dev/null < "/dev/tty${XDG_VTNR:-}")"

  # try to find free tty within range of 13..63
  [ "$Newxvt" ] || {
    for ((Newxvt=13 ; Newxvt<=63 ; Newxvt++)) ; do
      grep -q " $Newxvt " <<< "$Ttyinuse" || break
    done
  }

  [ "$Newxvt" ] || error "Could not identify a free tty for --xorg."

  [ "${XDG_VTNR:-}" ] && [ "$Hostdisplay$Hostwaylandsocket" ] && note "Current X server $Hostdisplay runs on tty ${XDG_VTNR:-}.
  Access it with [CTRL][ALT][F${XDG_VTNR:-}]."

  [ "${Newxvt:-999}" -gt "12" ] && {
    fgconsole --next-available 1>/dev/null 2>/dev/null || note "Could not check for a free tty below or equal to 12.
  Would need to use command fgconsole for a better check.
  Possibilities:
  1.) Run x11docker as root.
  2.) Add user to group tty (not recommended, may be insecure).
  3.) Use display manager gdm3.
  4.) Run x11docker directly from console."
    note "To access X on tty$Newxvt, use command 'chvt $Newxvt'"
  } || {
    note "New Xorg server $Newdisplay will run on tty $Newxvt.
  Access it with [CTRL][ALT][F$Newxvt]."
  }

  warning "On debian 9, switching often between multiple X servers can
  cause a crash of one X server. This bug may be debian specific and is probably
  some sort of race condition. If you know more about this or it occurs on
  other systems, too, please report at https://github.com/mviereck/x11docker.

  You can avoid this issue with switching to a black tty before switching to X."
  return 0
}
check_xcontainer() {            # option --xc: check image x11docker/xserver
  case "$Xcontainer" in
    no) ;;
    yes|auto) 
      case "$Backend" in
        host|proot|chroot)
          podman   inspect "$Xcontainerimage" >/dev/null 2>&1 && Xcontainerbackend="podman"
          [ -z "$Xcontainerbackend" ] && {
            docker inspect "$Xcontainerimage" >/dev/null 2>&1 && Xcontainerbackend="docker"
          }
          [ "$Xcontainer" = "yes" ] && [ -z "$Xcontainerbackend" ] && {
            note "Option --xc: Image $Xcontainerimage not found
  at backends podman or docker.
  Fallback: Disabling option --xc"
            check_fallback
            Xcontainer="no"
          }
          [ -n "$Xcontainerbackend" ] && Xcontainer="yes" || Xcontainer="no"
        ;;
        docker|podman|nerdctl)
          Xcontainerbackend="$Backend"
          $Xcontainerbackend inspect "$Xcontainerimage" >/dev/null 2>&1 || Xcontainerimage="" 
        ;;
      esac
    ;;
    docker|podman|nerdctl)
      Xcontainerbackend="$Xcontainer"
      Xcontainer="yes"
      $Xcontainerbackend inspect "$Xcontainerimage" >/dev/null 2>&1 || {
        note "Option --xc: Image $Xcontainerimage not found
  at backend '$Xcontainerbackend'.
  Fallback: Disabling option --xc"
        check_fallback
        Xcontainer="no"
      }
    ;;
  esac

  case "$Xcontainer" in
    yes)
      [ -z "$Xcontainerimage" ]     && Xcontainer="no"
      [ "$Runsonconsole" = "yes" ]  && Xcontainer="no"
      [ "$Winsubsystem" ]           && Xcontainer="no"
      [ "$Xcontainer" = "no" ] && {
        note "Option --xc not possible:
  - on console
  - on MS Windows
  - without image x11docker/xserver.
  - with runtime kata-runtime
  Fallback: Setting --xc=no"
        check_fallback
        Xcontainer="no"
      }
    ;;
    auto)
      case "$Runtime" in
        kata-runtime)                  Xcontainer="no" ;;
      esac
      [ -z "$Xcontainerimage" ]     && Xcontainer="no"
    ;;
  esac
  [ "$Xcontainer" = "yes" ] && Xtoolscontainer="yes"
  return 0
}
check_xdepends() {              # check dependencies on host for X server option $1
  # Return 1 if something is missing
  local Return= Message=

  [ "$Lastcheckedxserver" = "${1:-}" ] && debugnote "Dependencies of ${1:-} already checked: $Lastcheckedxserverresult " && return "$Lastcheckedxserverresult"

  case "$Autochooseserver" in
    yes) Message="debugnote" ;;
    no)  Message="note" ;;
  esac
  # xinit
  case "${1:-}" in
    --xephyr|--xpra|--nxagent|--xvfb|--xwayland|--weston-xwayland|--kwin-xwayland)
      [ "$Xcontainer" = "no" ] && {
        command -v xinit >/dev/null || {
          $Message "${1:-}: xinit not found."
          Return=1
        }
      }
    ;;
    --xorg)
      command -v xinit >/dev/null || {
        $Message "${1:-}: xinit not found."
        Return=1
      }
    ;;
  esac
  # Wayland
  case "${1:-}" in
    --xpra-xwayland|--xpra2-xwayland|--weston-xwayland|--xwayland|--weston|--kwin|--kwin-xwayland|--hostwayland)
      [ "$Nvidiaversion" ] && verlt "$Nvidiaversion" "470" && {
        $Message "${1:-}: Closed source NVIDIA driver $Nvidiaversion < version 470.x does not support Wayland.
  You would need NVIDIA driver>=v470.x and Xwayland>=21.1.2"
        Return=1
      }
      case "$Runtime" in
        kata-runtime)
          $Message "${1:-} is not supported with --runtime=$Runtime"
          Return=1
        ;;
      esac
      case "$Mobyvm" in
        yes)
          $Message "${1:-} is not supported with MobyVM / docker-for-win"
          Return=1
        ;;
      esac
    ;;
  esac
  case "${1:-}" in
    --weston|--kwin)
      [ "$Containeruser" != "$Hostuser" ] && {
        $Message "${1:-} does not run with a container user
  different from host user $Hostuser (option --user=$Containeruser)."
        Return=1
      }
    ;;
  esac
  # X command
  case "${1:-}" in
    --xpra2*)
      command -v "xpra" >/dev/null || {
        $Message "${1:-}: xpra not found on host.
  $Wikipackages"
        Return=1
      }
    ;;
  esac
  [ "$Xcontainer" = "no" ] && case "${1:-}" in
    --xpra)
      command -v "xpra" >/dev/null || {
        $Message "${1:-}: xpra not found.
  $Wikipackagesimage"
        Return=1
      }
      command -v "Xvfb" >/dev/null || {
        $Message "${1:-}: Xvfb not found.
  $Wikipackagesimage"
        Return=1
      } ;;
    --xpra-xwayland)
      command -v "xpra" >/dev/null || {
        $Message "${1:-}: xpra not found.
  $Wikipackagesimage"
        Return=1
      } ;;
    --xephyr)
      command -v "Xephyr" >/dev/null || {
        $Message "${1:-}: Xephyr not found.
  $Wikipackagesimage"
        Return=1
      } ;;
    --nxagent)
      command -v "nxagent" >/dev/null || {
        $Message "${1:-}: nxagent not found.
  $Wikipackagesimage"
        Return=1
      } ;;
    --xvfb)
      command -v "Xvfb" >/dev/null || {
        $Message "${1:-}: Xvfb not found.
  $Wikipackagesimage"
        Return=1
      } ;;
    --xorg)
      command -v "Xorg" >/dev/null || {
        $Message "${1:-}: Xorg not found.
  $Wikipackages"
        Return=1
      } ;;
    --xwin)
      case "$Winsubsystem" in
        CYGWIN)
          command -v XWin >/dev/null || {
            $Message "${1:-}: XWin not found.
  Need packages 'xinit', 'xauth' and 'xwininfo' in Cygwin (X11 section)."
            Return=1
          }
        ;;
        WSL1|WSL2)
          $Message "${1:-}: XWin is available in Cygwin on MS Windows only.
  Use runx to provide XWin in WSL:  https://github.com/mviereck/runx"
          Return=1
        ;;
        MSYS2)
          $Message "${1:-}: XWin is available in Cygwin on MS Windows only.
  With runx XWin is available in WSL, too.
  In MSYS2 you can only use runx with VcXsrv to provide an X server:
    https://github.com/mviereck/runx"
          Return=1
        ;;
        "")
          $Message "${1:-}: XWin is available in Cygwin on MS Windows only."
          Return=1
        ;;
      esac
      command -v xwininfo >/dev/null || {
        $Message "${1:-}: xwininfo not found.
  Need 'xwininfo' package from Cygwin/X (X11 section)."
        Return=1
      }
      [ "$Hostip" ] || {
        $Message "${1:-}: Failed to get host IP address."
        Return=1
      }
    ;;
    --runx)
      [ "$Winsubsystem" ] || {
        $Message "${1:-}: runx is available on MS Windows only."
        Return=1
      }
      command -v runx >/dev/null || {
        $Message "${1:-}: runx not found.
  Need runx from https://github.com/mviereck/runx"
        Return=1
      }
    ;;
  esac
  # Wayland command
  [ "$Xcontainer" = "no" ] && case "${1:-}" in
    --weston|--xpra-xwayland|--weston-xwayland)
      command -v "weston" >/dev/null || {
        $Message "${1:-}: weston not found.
  $Wikipackagesimage"
        Return=1
      } ;;
    --kwin|--kwin-xwayland)
      command -v "kwin_wayland" >/dev/null || {
        $Message "${1:-}: kwin_wayland not found.
  $Wikipackages"
        Return=1
      } ;;
  esac
  [ "$Xcontainer" = "no" ] && case "${1:-}" in
    --xpra-xwayland|--weston-xwayland|--kwin-xwayland|--xwayland)
      command -v "Xwayland" >/dev/null || {
        $Message "${1:-}: Xwayland not found.
  $Wikipackagesimage"
        Return=1
      } ;;
  esac
  [ "$Xcontainer" = "no" ] && case "${1:-}" in
    --xpra-xwayland)
      command -v "xdotool" >/dev/null || {
        $Message "${1:-}: xdotool not found.
  $Wikipackagesimage"
        Return=1
      } ;;
  esac
  # xpra version
  [ "$Xcontainer" = "no" ] && case "${1:-}" in
    --xpra*)
      [ "$Return" = "1" ] || {
        [ "$Xpraversion" ] || {
          Xpraversion="$(xpra --version 2>/dev/null | cut -d' ' -f2)"
          verbose "Xpra version: ${Xpraversion:-XPRA_NOT_FOUND}"
          [ "$Xprahelp" ] || Xprahelp="$(xpra --help 2>/dev/null)"
        }
      }
    ;;
  esac
  case "${1:-}" in
    --hostdisplay|--xpra-xwayland|--xpra2-xwayland|--xephyr|--nxagent)
      [ "$Hostdisplay" ] || {
        $Message "${1:-} needs a running X server. DISPLAY is empty."
        Return=1
      }
    ;;
    --hostwayland|--xwayland)
      [ "$Hostwaylandsocket" ] || {
        $Message "${1:-} needs a running Wayland compositor. WAYLAND_DISPLAY is empty."
        Return=1
      }
    ;;
  esac
  [ "$Winsubsystem" ] && {
    case "${1:-}" in
      --tty|--xwin|--runx) ;;
      --xpra*)
        $Message "${1:-} is not supported on MS Windows."
        Return=1
      ;;
      *)
        [ -z "$Hostdisplay" ] && {
          case "$Winsubsystem" in
            Cygwin) $Message "${1:-} needs a running X server. DISPLAY is empty.
  Please install packages in Cygwin:  xinit xauth xwininfo
  or use runx to provide an X server on MS Windows:
    https://github.com/mviereck/runx" ;;
            MSYS2|WSL1|WSL2) $Message "${1:-} needs a running X server. DISPLAY is empty.
  Please use runx to provide an X server on MS Windows:
    https://github.com/mviereck/runx" ;;
          esac
          Return=1
        }
      ;;
    esac
  }
  # --xoverip
  [ "$Xoverip" = "yes" ] && {
    case "${1:-}" in
      --xephyr|--xorg|--nxagent|--xpra|--xpra2|--xvfb|--xwin|--runx|--tty) ;;
      --hostdisplay)
        [ -n "$(cut -d: -f1 <<< "$Hostdisplay")" ] || {
          $Message "${1:-} does not support X over TCP
  except if started remotely with 'ssh -X'."
          Return=1
        }
      ;;
      --xwayland|--weston-xwayland|--kwin-xwayland|--xpra-xwayland|--xpra2-xwayland)
        $Message "${1:-} does not support X over TCP (--xoverip)."
        Return=1
      ;;
      --weston|--kwin|--hostwayland)
        $Message "${1:-} does not support X over TCP (--xoverip)."
      ;;
    esac
  }
  # --gpu
  [ "$Sharegpu" = "yes" ] && {
    case "${1:-}" in
      --hostdisplay|--xorg|--xpra-xwayland|--xpra2-xwayland|--weston-xwayland|--kwin-xwayland|--xwayland|--xwin|--runx) ;;
      --weston|--kwin|--hostwayland|--tty) ;;
      *)
        $Message "${1:-} does not support hardware acceleration (option --gpu)."
        Return=1
      ;;
    esac
  }
  # --xc
  case "$Xcontainer" in
    yes)
      grep -q -w -- "$Xserver" <<< "$Xcontainer_supportlist" && {
        [ "$Runsonconsole" = "yes" ] && {
          $Message "${1:-} with option --xc does not run on console."
          Return=1
        }
        :
      } || {
        $Message "${1:-} not supported yet to run inside of container."
        Return=1
      }
    ;;
    no)
      case "${1:-}" in
        --xpra2*)
          $Message "${1:-} needs image $Xcontainerimage (option --xc)."
          Return=1
        ;;
      esac
    ;;
  esac

  Return="${Return:-"0"}"
  debugnote "Dependency check for ${1:-}: $Return"

  [ "$Return" = "1" ] && {
    check_fallback
    Autochooseserver="yes"
  }

  Lastcheckedxserver="${1:-}"
  Lastcheckedxserverresult="$Return"

  return "$Return"
}
check_xpraoption() {            # check if xpra option $1 is available
  local Option

  [ "$Xcontainer" = "yes" ] && [ "$Xserver" != "--xpra2" ] && {
    echo "$@"
    return 0
   }

  Option="$(cut -d= -f1 <<< "${1:-}")"
  grep -q "noprobe" <<< "${1:-}" && {
    grep "OpenGL" <<< "$Xprahelp" | grep -q "probe" && echo "$@" || {
      debugnote "Xpra option $Option not supported: $*"
      return 1
    }
    return 0
  }
  grep -q -- "$Option" <<< "$Xprahelp" && echo "$@" || {
    debugnote "Xpra option not found: $Option"
    return 1
  }
}
check_xserver() {               # check chosen X server, auto-choose X server
  local Xorgautochoice=

  [ -n "$Xserver" ] && Autochooseserver="no"
  ## default option '--auto': Try to automatically choose best matching and available X server
  [ "$Autochooseserver" = "yes" ] && {                              Xserver="--xpra2"
    [ "$Sharegpu" = "yes" ]                                      && Xserver="--xpra2-xwayland"
    [ "$Xfishtank" = "yes" ]                                     && Xserver="--xephyr"
    [ "$Desktopmode" = "yes"  ]                                  && Xserver="--xephyr"
    [ "$Xserver" = "--xephyr" ] && { check_xdepends --xephyr     || Xserver="--weston-xwayland" ; }  ### FIXME: don't use check_xdepends() here
    [ "$Sharegpu" = "yes" ]     && [ "$Xserver" = "--xephyr" ]   && Xserver="--weston-xwayland"
    [ "$Outputcount" != "1" ]                                    && Xserver="--weston-xwayland"
    [ -n "$Rotation" ]                                           && Xserver="--weston-xwayland"
    [ "$Scaling" ]              && [ "$Sharegpu" = "yes" ]       && Xserver="--xpra2-xwayland"
    [ "$Scaling" ]              && [ "$Sharegpu" = "no" ]        && Xserver="--xpra2"
    [ "$Runsonconsole" = "yes" ]                                 && Xserver="--xorg"
    [ -z "$Hostdisplay" ]       && [ -n "$Hostwaylandsocket" ]   && Xserver="--xpra2"
    [ -z "$Hostdisplay" ]       && [ -n "$Hostwaylandsocket" ]   && [ "$Desktopmode" = "yes"  ]  && Xserver="--weston-xwayland"
    [ "$Winsubsystem" ]                                          && Xserver="--runx"
    [ "$Winsubsystem" = "CYGWIN" ]                               && Xserver="--xwin"
    [ "$Setupwayland" = "yes" ] && { [ -n "$Hostwaylandsocket" ] && [ "$Desktopmode" = "no" ] && Xserver="--hostwayland" || Xserver="--weston" ; }
  }

  [ "$Sharegpu" = "yes" ] && {
    case "$Xserver" in
      --xpra|--xpra2)
        note "Option --xpra does not support GPU access.
  Fallback: Will try to use option --xpra2-xwayland."
        check_fallback
        Xserver="--xpra2-xwayland"
      ;;
      --xephyr)
        note "Option --xephyr does not support GPU access.
  Fallback: Will try to use option --weston-xwayland."
        check_fallback
        Xserver="--weston-xwayland"
      ;;
      --nxagent)
        case "$Desktopmode" in
          yes) Xserver="--weston-xwayland" ;;
          no)  Xserver="--xpra2-xwayland" ;;
        esac
        note "Option --nxagent does not support GPU access.
  Fallback: Will try to use option $Xserver."
        check_fallback
      ;;
    esac
  }

  grep -q -i "GNOME" <<< "$XDG_CURRENT_DESKTOP" && {
    Gnomeversion="$(gnome-shell --version)"
    [ "$Gnomeversion" ] && verlt "$Gnomeversion" "GNOME Shell 3.38" && {
      case "$Xserver" in
        --hostdisplay|--xorg|--tty) ;;
        *)
          warning "You are running GNOME desktop in outdated version
  $Gnomeversion
  This might cause issues with host applications if using additional X servers.
  It is recommended to use another desktop environment or GNOME >= 3.38.
  Only --xorg or discouraged option --hostdisplay might work as expected."
          [ "$Autochooseserver" = "yes" ] && case "$Desktopmode" in
            "yes") Xserver="--xorg" && Xorgautochoice="yes" ;;
            "no")  Xserver="--hostdisplay" ;;
          esac
        ;;
      esac
    }
  }

  # X over TCP
  [ -z "$Xoverip" ] && {
    case "$Xserver" in
      --tty|--weston|--kwin|--hostwayland) ;;
      *)
        case "$Runtime" in
          kata-runtime)
            Xoverip="yes"
            [ "$Network" = "none" ] && {
              note "Option --runtime=$Runtime works only with X over IP.
  Enabling option --xoverip."
            }
          ;;
        esac
      ;;
    esac
    [ "$Xoverip" = "yes" ] && [ "$Autochooseserver" = "no" ] && debugnote "Enabled X over TCP instead of sharing unix socket."
  }

  [ "$Nvidiaversion" ] && [ "$Sharegpu" = "yes" ] && case "$Xserver" in
    --xpra-xwayland|--xpra2-xwayland|--weston-xwayland|--xwayland|--weston|--kwin|--kwin-xwayland|--hostwayland)
      verlt "$Nvidiaversion" "470" && note "Option $Xserver --gpu: Your system uses closed source NVIDIA
  driver version $Nvidiaversion.
  GPU support will work only with options --hostdisplay and --xorg.
  Consider to use free open source driver nouveau instead.
  Using NVIDIA driver>=v470.x  and Xwayland>=v21.1.2 might work for $Xserver --gpu."
    ;;
  esac

  [ "$Runsonconsole" = "no" ] && [ "$Runsoverssh" = "no" ] && [ -z "$Hostdisplay$Hostwaylandsocket" ] && [ "$Xserver" != "--tty" ] && [ -z "$Winsubsystem" ] && {
    warning "Environment variables DISPLAY and WAYLAND_DISPLAY are empty,
  but it seems x11docker was started within X, not from console.
  Please set DISPLAY and XAUTHORITY.
  If you have started x11docker with su or sudo, su/sudo may be configured to
  unset X environment variables. It may work if you run x11docker with
    sudo -E x11docker [...]
  If your system does not support 'sudo -E', you can try
    sudo env DISPLAY=\$DISPLAY XAUTHORITY=\$XAUTHORITY x11docker [...]
  Otherwise, you can use tools like gksu/gksudo/kdesu/kdesudo/lxsu/lxsudo."

    [ -n "${PKEXEC_UID:-}" ] && note "It seems you have started x11docker with pkexec.
  Can not determine DISPLAY and XAUTHORITY, can not use your X server.
  To allow other X server options, please provide environment variables with
    pkexec env DISPLAY=\$DISPLAY XAUTHORITY=\$XAUTHORITY x11docker [ARGS]."

    [ "$Autochooseserver" = "yes" ] && Xserver="--xorg" && Xorgautochoice="yes"
  }

  [ "$Runsoverssh" = "yes" ] && [ -z "$Hostdisplay$Hostwaylandsocket" ] && [ "$Xserver" != "--tty" ] && {
    error "You are running x11docker over SSH without providing a display.
  DISPLAY and WAYLAND_DISPLAY are empty.
  Please run with 'ssh -X' or 'ssh -Y'.
  If there is already an X or Wayland session running on the remote system
  that you want to use,  please set either DISPLAY and XAUTHORITY 
  or WAYLAND_DISPLAY and XDG_RUNTIME_DIR accordingly."
  }

  ## check if dependencies for chosen X server are installed, fall back to best alternatives if not
  [ "$Xserver" = "--xwin" ]            && { check_xdepends --xwin            || Xserver="--runx" ; }
  [ "$Xserver" = "--runx" ]            && { check_xdepends --runx            || Xserver="--hostdisplay" ; }
  [ "$Xserver" = "--hostdisplay" ]     && { check_xdepends --hostdisplay     || Xserver="--xpra2" ; }
  [ "$Xserver" = "--xephyr" ]          && { check_xdepends --xephyr          || Xserver="--nxagent" ; }
  [ "$Xserver" = "--hostwayland" ]     && { check_xdepends --hostwayland     || Xserver="--weston"  ; }
  [ "$Xserver" = "--nxagent" ]         && { check_xdepends --nxagent         || { [ "$Desktopmode" = "yes" ] && Xserver="--xephyr"        || Xserver="--xpra2" ; } ; }
  [ "$Xserver" = "--xpra2" ]           && { check_xdepends --xpra2           || Xserver="--xpra" ; }
  [ "$Xserver" = "--xpra" ]            && { check_xdepends --xpra            || { check_xdepends --nxagent   && Xserver="--nxagent"       || Xserver="--xephyr" ; } ; }
  [ "$Xserver" = "--xorg" ]            && { check_xdepends --xorg            || Xserver="--weston-xwayland" ; }
  [ "$Xserver" = "--xpra2-xwayland" ]  && { check_xdepends --xpra2-xwayland  || Xserver="--xpra-xwayland" ; }
  [ "$Xserver" = "--xpra-xwayland" ]   && { check_xdepends --xpra-xwayland   || Xserver="--weston-xwayland" ; }
  [ "$Xserver" = "--xwayland" ]        && { check_xdepends --xwayland        || Xserver="--weston-xwayland" ; }
  #[ "$Xserver" = "--xpra-xwayland" ]   && { check_xdepends --xpra-xwayland   || { [ "$Desktopmode" = "yes" ] && Xserver="--kwin-xwayland" || Xserver="--hostdisplay" ; } ; }
  [ "$Xserver" = "--kwin-xwayland" ]   && { check_xdepends --kwin-xwayland   || Xserver="--weston-xwayland" ; }
  [ "$Xserver" = "--kwin" ]            && { check_xdepends --kwin            || Xserver="--weston" ; }
  #[ "$Xserver" = "--weston-xwayland" ] && { check_xdepends --weston-xwayland || Xserver="--kwin-xwayland" ; }
  [ "$Xserver" = "--weston" ]          && { check_xdepends --weston          || Xserver="--kwin" ; }

  case "$Xserver" in
    --weston|--kwin|--hostwayland) Setupwayland="yes" ;;
  esac
  [ "$Setupwayland" = "yes" ]          && { Autochooseserver="no" check_xdepends $Xserver || error "Failed to set up a Wayland environment.
  Please install 'weston' or 'kwin_wayland'." ; }

   # Xephyr as fallback for all options. Last fallback: Xorg
  check_xdepends $Xserver || Xserver="--xephyr"
  [ "$Xserver" = "--xephyr" ] && {          check_xdepends --xephyr || {
                                              #check_xdepends --kwin-xwayland   && Xserver="--kwin-xwayland"
                                              check_xdepends --hostdisplay     && [ "$Desktopmode" = "no" ] && Xserver="--hostdisplay"
                                              check_xdepends --runx            && Xserver="--runx"
                                              check_xdepends --xwin            && Xserver="--xwin"
                                              check_xdepends --nxagent         && Xserver="--nxagent"
                                              check_xdepends --weston-xwayland && Xserver="--weston-xwayland"
                                              check_xdepends --xpra            && Xserver="--xpra"
                                              check_xdepends --xpra2           && Xserver="--xpra2"
                                            }
    [ "$Sharegpu" = "yes" ] && case "$Desktopmode" in
      yes)                                  check_xdepends --weston-xwayland && Xserver="--weston-xwayland" ;;
      no)                                   check_xdepends --hostdisplay     && Xserver="--hostdisplay" ;;
    esac
    [ "$Runsonconsole" = "yes" ] && {
                                            #check_xdepends --kwin-xwayland   && Xserver="--kwin-xwayland"
                                            check_xdepends --weston-xwayland && Xserver="--weston-xwayland"
                                            check_xdepends --xorg            && Xserver="--xorg"
    }
    check_xdepends $Xserver || { Xserver="--xorg" && [ "$Autochooseserver" = "yes" ] && Xorgautochoice="yes" ; }
  }

  Autochooseserver="no" check_xdepends $Xserver || {
    case "$Winsubsystem" in
      "")
        error "Did not find a possibility to provide a display.
  Recommendations:
    To run within an already running X server,
    pull or build image $Xcontainerimage,
    or install 'xinit' and one or all of:
      Xephyr xpra nxagent
    To run with GPU acceleration, install:
      weston and Xwayland, optionally also: xpra and xdotool
    To run from TTY or within Wayland, install:
      weston and Xwayland
  $Wikipackagesimage"
      ;;
      CYGWIN)
        error "Did not find a possibility to provide a display.
  Please install packages 'xinit' and 'xauth' in Cygwin,
  or run x11docker with --runx:  https://github.com/mviereck/runx"
      ;;
      MSYS2|WSL1|WSL2)
        [ "$Hostdisplay" ] && {
          error "Did not find a possibility to provide a nested display.
  Please install package 'xinit' and one or all of:  nxagent Xephyr xpra
  $Wikipackages"
        } || {
        error "Did not find a possibility to provide a display.
  Please use --runx to provide an X server on MS Windows:
    https://github.com/mviereck/runx"
        }
      ;;
    esac
  }

  [ "$Xserver" = "--xorg" ] && [ "$Xorgautochoice" = "yes" ] && [ "$Runsonconsole" = "no" ] && error "Option --xorg was chosen automatically
  as the best one fitting your specified options and installed dependencies.
  However, x11docker does not run another Xorg without being specified.
  Please run with option --xorg."

  [ "$Autochooseserver" = "yes" ] && note "Using X server option $Xserver"
  storeinfo "xserver=$Xserver"

  [ "$Xcontainer" = "auto" ] && {
    grep -q -w -- "$Xserver" <<< "$Xcontainer_supportlist" && Xcontainer="yes" || Xcontainer="no"
    [ "$Xcontainer" = "yes" ] && note "Option --xc enabled automatically.
  X server will run in container of image $Xcontainerimage.
  You can disable this with --xc=no"
  }

  return 0
}
create_modelinefile() {         # generate a set of smaller modelines for screen size $1 and store them in a cache file
  local Newmodelinefile Modeline Size X Y Xcount Ycount

  Size="${1:-}"
  X="$(echo "$Size" | cut -dx -f1)"
  Y="$(echo "$Size" | cut -dx -f2)"
  Newmodelinefile="$Modelinefilebasepath/$Size"

  [ -e "$Newmodelinefile" ] || {
    debugnote "$Xserver: Generating modelines for $Size"
    mkfile "$Newmodelinefile"
    for Ycount in 25 30 40 45 50 55 60 65 70 75 80 85 90 95 100; do
      for Xcount in 25 30 40 45 50 55 60 65 70 75 80 85 90 95 100; do
        Modeline="$(cvt "$(awk -v a="$X" -v b="$Xcount" 'BEGIN {print (a * b / 100)}')" "$(awk -v a="$Y" -v b="$Ycount" 'BEGIN {print (a * b / 100)}' )" | tail -n1)"
        Modeline="${Modeline//"_60.00"/""}"
        echo "$Modeline" >> "$Newmodelinefile"
      done
    done
  }

  echo "$Newmodelinefile"
}
create_xcommand() {             ### create command to start X server and/or Wayland compositor
  local Xserveroptions_custom= Xpraoptions= Nxagentoptions= Compositorpid= Weston= Westonoutput= Count= Usemitshm

  Xserveroptions_custom="$Xserveroptions"
  Xserveroptions=""

  [ "$Xcontainer" = "yes" ] && [ "$Xcontainerbackend" = "$Backend" ] && Usemitshm="yes"
  [ "$Shareipc" = "host" ]                                           && Usemitshm="yes"
  [ "$Xcontainer" = "no" ] &&[ "$Shareipc" != "host" ]               && Usemitshm="no"
  [ "$Containeruser" != "$Hostuser" ]                                && Usemitshm="no"
  [ "$Xoverip" = "yes" ]                                             && Usemitshm="no"
  case "$Runtime" in
    kata-runtime|sysbox-runc)                                           Usemitshm="no" ;;
  esac

  #### General X server options
  case "$Xserver" in
    --nxagent)
      case "$Usemitshm" in
        yes) Xserveroptions="$Xserveroptions \\
  -shmem \\
  -shpix" ;;
        no) Xserveroptions="$Xserveroptions \\
  -noshmem \\
  -noshpix" ;;
      esac
    ;;
    *)
      Xserveroptions=" \\
  -retro \\
  +extension RANDR \\
  +extension RENDER \\
  +extension GLX \\
  +extension XVideo \\
  +extension DOUBLE-BUFFER \\
  +extension SECURITY \\
  +extension DAMAGE \\
  +extension X-Resource \\
  -extension XINERAMA -xinerama"
      case "$Usemitshm" in
        yes)
          Xserveroptions="$Xserveroptions \\
  +extension MIT-SHM" ;;
        no)
          Xserveroptions="$Xserveroptions \\
  -extension MIT-SHM"
          Xprashm="XPRA_XSHM=0"
        ;;
      esac
    ;;
  esac

  # X extension COMPOSITE
  [ "$Xcomposite" ] || case "$Xserver" in
    --nxagent|--xwin) Xcomposite="no" ;;
    *) Xcomposite="yes" ;;
  esac
  case "$Xcomposite" in
    yes)
      # Old X servers have extension "Composite", recent ones call it "COMPOSITE".
      Xserveroptions="$Xserveroptions \\
  +extension Composite +extension COMPOSITE"
    ;;
    no)
      Xserveroptions="$Xserveroptions \\
  -extension Composite -extension COMPOSITE"
      [ "$Xserver" = "nxagent" ] && Xserveroptions="Xserveroptions \\
  -nocomposite"
    ;;
  esac

  # X extension XTEST
  [ "$Xtest" ] || case "$Xserver" in
    --xpra*|--xvfb) Xtest="yes" ;;
    *) Xtest="no" ;;
  esac
  case "$Xtest" in
    yes) Xserveroptions="$Xserveroptions \\
  +extension XTEST" ;;
    no)  Xserveroptions="$Xserveroptions \\
  -extension XTEST -tst" ;;
  esac

  # Disable screensaver
  Xserveroptions="$Xserveroptions \\
  -dpms \\
  -s off"

  # X cookie authentication
  case "$Xauthentication" in
    yes)
      Xserveroptions="$Xserveroptions \\
  -auth $Xservercookie" ;;
    no)
  Xserveroptions="$Xserveroptions \\
  -ac"
      case "$Xoverip" in
        yes) warning "Option --no-auth: SECURITY RISK!
  Allowing access to new X server for everyone.
  Your X server is accessible over TCP network without any restriction.
  That can be abused to take control over your system." ;;
        no|"")
          case "$Xserver" in
            --hostdisplay|--hostwayland|--weston|--kwin|--tty) ;;
            *) warning "Option --no-auth: SECURITY RISK!
  Allowing access to new X server for everyone." ;;
          esac
        ;;
      esac
    ;;
  esac

  # X over IP/TCP
  case "$Xoverip" in
    yes)
      case "$Xserver" in
        --nxagent) ;;
        *) Xserveroptions="$Xserveroptions \\
  -listen tcp" ;;
      esac
    ;;
    no|"") Xserveroptions="$Xserveroptions \\
  -nolisten tcp" ;;
  esac

  # check DPI
  case "$Xserver" in
    --weston|--kwin|--tty|--hostdisplay) ;;
    --xwin|--runx) ;;
    *)
      [ -z "$Dpi" ] && {
        xdpyinfo >/dev/null 2>&1 && {
          Dpi="$(xdpyinfo | grep dots | cut -dx -f2 | cut -d' ' -f1)"
        } || {
          [ -n "$Hostdisplay" ] && [ -z "$(command -v xdpyinfo)" ] && note "Could not determine dpi settings. If you encounter too big or
  too small fonts with $Xserver, please install xdpyinfo or use option --dpi.
  $Wikipackages"
        }
        case "$Xserver" in
          --xpra*)
            [ "$Scaling" ] && [ "$Desktopmode" = "no" ] && {
              Dpi="$(awk -v a="$Scaling" -v b="$Dpi" 'BEGIN {print (b * a * a)}')"
              Dpi="${Dpi%.*}"
            } ;;
        esac
      }
    ;;
  esac
  [ -n "$Dpi" ] && Xserveroptions="$Xserveroptions \\
  -dpi $Dpi"


  #### xpra server and client command
  case "$Xserver" in
    --xpra*)
      [ "$Xprahelp" ] || Xprahelp="$(xpra --help 2>/dev/null)"

#  $(check_xpraoption --csc-modules=none) \\
#  $(check_xpraoption --encodings=rgb) \\
      Xpraoptions="\\
  $(check_xpraoption --microphone=no) \\
  $(check_xpraoption --mmap=$Sharefolder/xpra.mmap) \\
  $(check_xpraoption --notifications=no) \\
  $(check_xpraoption --pulseaudio=no) \\
  $(check_xpraoption --socket-dirs="$Sharefolder") \\
  $(check_xpraoption --speaker=no) \\
  $(check_xpraoption --start-via-proxy=no) \\
  $(check_xpraoption --system-tray=yes) \\
  $(check_xpraoption --webcam=no) \\
  $(check_xpraoption --xsettings=no)"
#  $(check_xpraoption --clipboard-direction=both) \\

      # --keymap
      [ "$Xkblayout" ] && Xpraoptions="$Xpraoptions \\
  $(check_xpraoption --keyboard-layout="'$Xkblayout'") \\
  $(check_xpraoption --keyboard-raw=yes)"

#      Xpraoptions="$Xpraoptions  $(check_xpraoption --debug=all)" ; Preservecachefiles="yes"  # Debugging only

      # xpra server command
      [ "$Desktopmode" = "yes" ] && Xpraservercommand="xpra start-desktop" || Xpraservercommand="xpra start"
      Xpraservercommand="$Xpraservercommand :$Newdisplaynumber --use-display $Xpraoptions \\
  $(check_xpraoption --clipboard=yes)\\
  $(check_xpraoption --dbus-launch=) \\
  $(check_xpraoption --dbus-proxy=no) \\
  $(check_xpraoption --daemon=no) \\
  $(check_xpraoption --fake-xinerama=no) \\
  $(check_xpraoption --file-transfer=off) \\
  $(check_xpraoption --html=off) \\
  $(check_xpraoption --opengl=noprobe) \\
  $(check_xpraoption --mdns=no) \\
  $(check_xpraoption --printing=no) \\
  $(check_xpraoption --session-name="$Codename") \\
  $(check_xpraoption --start-new-commands=no) \\
  $(check_xpraoption --systemd-run=no)"
#  $(check_xpraoption --video-encoders=none)"
      [ -n "$Dpi" ] && Xpraservercommand="$Xpraservercommand \\
  $(check_xpraoption --dpi="$Dpi")"
      case "$Xserver" in
        --xpra2*)  Xpraservercommand="$Xpraservercommand \\
  --bind=$Sharefolder/$(hostname)-$Newdisplaynumber" ;;
      esac

      # xpra client command
#  $(check_xpraoption --compress=0) \\
#  $(check_xpraoption --quality=100) \\
#  $(check_xpraoption --video-decoders=none)"
      Xpraclientcommand="xpra attach :$Newdisplaynumber $Xpraoptions \\
  $(check_xpraoption --clipboard=$Shareclipboard) \\
  $(check_xpraoption --modal-windows=no) \\
  $(check_xpraoption --opengl=noprobe) \\
  $(check_xpraoption --reconnect=no) \\
  $(check_xpraoption --tray=no)"
      [ "$Fullscreen" = "yes" ]  && Xpraclientcommand="$Xpraclientcommand \\
  $(check_xpraoption --desktop-fullscreen=yes)"
      [ "$Scaling" ] && Xpraclientcommand="$Xpraclientcommand \\
  $(check_xpraoption --desktop-scaling="$Scaling")"
#      [ -n "$Dpi" ] && Xpraclientcommand="$Xpraclientcommand \\
#  $(check_xpraoption --dpi="'$Dpi'")"
      [ "$Xpraborder" ] && Xpraclientcommand="$Xpraclientcommand \\
  $(check_xpraoption --border="$Xpraborder")"
#        case "$Desktopmode" in ### FIXME
#          yes) Xpraclientcommand="$Xpraclientcommand \\
#  $(check_xpraoption --title="'$Codename on $Newdisplay [in container] (shift+F11 toggles fullscreen)'")" ;;
#          no)  Xpraclientcommand="$Xpraclientcommand \\
#  $(check_xpraoption --title="'@title@ [in container]'")" ;;
#        esac

      # xpra environment variables
      for Line in $Xpracontainerenv; do
        store_runoption env "$Line"
      done
    ;;
  esac


  #### Prepare weston.ini: config file for Weston
  case "$Xserver" in
    --weston|--weston-xwayland|--xpra-xwayland|--xpra2-xwayland)
      command -v weston-launch >/dev/null && [ "$Runsonconsole" = "yes" ] && [ "$Runsinteractive" = "yes" ] && Weston="weston-launch -v --" || Weston="weston"
      echo "[core]
shell=desktop-shell.so
idle-time=0
[shell]
panel-location=none
panel-position=none
locking=false
background-color=0xff002244
animation=fade
startup-animation=fade
[keyboard]
" >> "$Westonini"
      # --keymap: keyboard layout
      [ -n "$Xkblayout" ] && echo "keymap_layout=$Xkblayout" >> "$Westonini"
      [ -z "$Xkblayout" ] && [ "$Runsonconsole" = "yes" ] && echo "$(echo -n "keymap_layout=" && grep XKBLAYOUT <"/etc/default/keyboard" | cut -d= -f2 | cut -d'"' -f2)" >> "$Westonini"

      case "$Runsonconsole" in
        no)   # Display prefix X or WL; needed to indicate if host Wayland or host X provides the nested window.
          #[ -n "$Hostwaylandsocket" ] && [ "$Xserver" != "--xpra-xwayland" ] && [ "$Hostsystem" != "ubuntu" ] && [ "$Fullscreen" = "no" ] && Westonoutput="WL"
          [ -n "$Hostdisplay" ] && Westonoutput="X"
          [ -z "$Westonoutput" ] && [ -n "$Hostwaylandsocket" ] && Westonoutput="WL"
        ;;
        yes)  # short start&stop of Weston to grep name of monitor. Needed instead of WL or X prefix
          [ -n "$Screensize" ] || [ "$Scaling" ] || [ -n "$Rotation" ] && {
            Westonoutput="$(weston_getoutputname)"
            debugnote "$Xserver: Detected screen output for weston: $Westonoutput"
          }
        ;;
      esac
    ;;
  esac


  #### create command to run X server
  case "$Xserver" in
    --xorg)
      Xserveroptions="$Xserveroptions \\
  -verbose"
      [ "$Xorgconf" ] && Xserveroptions="$Xserveroptions \\
  -config '$Xorgconf'"       # --xorgconf
#      [ "$Runsonconsole" = "yes" ] && Xserveroptions="$Xserveroptions \\
#  -keeptty"
      Xcommand="$(get_xpath Xorg) :$Newdisplaynumber vt$Newxvt $Xserveroptions"
    ;;
    --xpra|--xpra2)
      Xcommand="$(get_xpath Xvfb) :$Newdisplaynumber $Xserveroptions \\
  -screen 0 ${Xaxis}x${Yaxis}x24"
    ;;

    --xvfb)
      Xcommand="$(get_xpath Xvfb) :$Newdisplaynumber $Xserveroptions \\
  -screen 0 ${Screensize}x24"   ### FIXME: hardcoded setting of depth 24. Could be better?
    ;;

    --xephyr)
      Xserveroptions="$Xserveroptions \\
  -resizeable \\
  -noxv"
#      Xserveroptions="$Xserveroptions \\
#  -glamor"  # disabled because of lagginess reported in #196
      case "$Fullscreen" in
        yes)
          Xserveroptions="$Xserveroptions \\
  -fullscreen"
        ;;
        no)
          grep -q -- "-output " <<< "$Xserveroptions_custom" || Xserveroptions="$Xserveroptions \\
  -screen $Screensize"
        ;;
      esac
      Xcommand="$(get_xpath Xephyr) :$Newdisplaynumber $Xserveroptions"
   ;;

    --xwayland)
      Xcommand="$(get_xpath Xwayland) :$Newdisplaynumber -ac $Xserveroptions"
    ;;

    --xpra-xwayland|--xpra2-xwayland)
      Xcommand="$(get_xpath Xwayland) :$Newdisplaynumber -ac $Xserveroptions"

      echo "[output]
name=${Westonoutput}1
mode=$Screensize" >> "$Westonini"
      [ -n "$Customwestonini" ] && Westonini="$Customwestonini"

      Compositorcommand="$Weston \\
  --socket=$Newwaylandsocket \\
  --backend=x11-backend.so \\
  --config='$Westonini'"
      case "$Xserver" in
        --xpra-xwayland|--xpra2-xwayland)
          case "$Scaling" in
            "") Compositorcommand="$Compositorcommand \\
  --fullscreen" ;;
            *)  Compositorcommand="$Compositorcommand \\
  --width=$(cut -dx -f1 <<< "$Screensize") --height=$(cut -dx -f2 <<< "$Screensize")" ;;
          esac
        ;;
      esac
    ;;
    --weston|--weston-xwayland)
      Xcommand="$(get_xpath Xwayland) :$Newdisplaynumber -ac $Xserveroptions"

      [ -n "$Westonoutput" ] && for ((Count=1 ; Count<="$Outputcount" ; Count++)) ; do
        [ "$Westonoutput" = "WL" ] || [ "$Westonoutput" = "X" ] || {
          Count=""
          [ -z "$Screensize" ] && Screensize="preferred"
        }
        echo "[output]
name=$Westonoutput$Count
mode=$Screensize
" >> "$Westonini"
        [ "$Scaling" ]        && echo "scale=$Scaling"      >> "$Westonini"
        [ -n "$Rotation" ]    && echo "transform=$Rotation" >> "$Westonini"
        [ "$Count" ] || break
      done

      Compositorcommand="$Weston \\
  --socket=$Newwaylandsocket"
      [ "$Fullscreen" = "yes" ] && Compositorcommand="$Compositorcommand \\
  --fullscreen"
      [ "$Outputcount" = "1" ]  || Compositorcommand="$Compositorcommand \\
  --output-count=$Outputcount"
      case "$Westonoutput" in
        WL) Compositorcommand="$Compositorcommand \\
  --backend=wayland-backend.so" ;;
        X)  Compositorcommand="$Compositorcommand \\
  --backend=x11-backend.so" ;;
        *)
          case "$Runsonconsole" in
            yes) Compositorcommand="$Compositorcommand \\
  --backend=drm-backend.so" ;;
            no)  Compositorcommand="$Compositorcommand \\
  --backend=x11-backend.so" ;;
          esac
        ;;
      esac
      [ -n "$Customwestonini" ] && Westonini="$Customwestonini"
      Compositorcommand="$Compositorcommand \\
  --config='$Westonini'"
    ;;

    --kwin|--kwin-xwayland)
      Xcommand="$(get_xpath Xwayland) :$Newdisplaynumber -ac $Xserveroptions"

      Compositorcommand="kwin_wayland \\
  --xwayland \\
  --socket=$Newwaylandsocket \\
  --width=$Xaxis --height=$Yaxis"
      [ "$Outputcount" = "1" ] || Compositorcommand="$Compositorcommand \\
  --output-count=$Outputcount"
      [ "$Xkblayout" ] && Compositorcommand="KWIN_XKB_DEFAULT_KEYMAP=$Xkblayout $Compositorcommand"
      Compositorcommand="env QT_XKB_CONFIG_ROOT=/usr/share/X11/xkb $Compositorcommand"
      case "$Runsonconsole" in
        yes) Compositorcommand="$Compositorcommand \\
  --drm" ;;
        no)
          kwin_wayland --help | grep -q -- '--windowed' && {
           Compositorcommand="$Compositorcommand \\
  --windowed"
          } || {
            kwin_wayland --help | grep -q -- '--x11-display' && [ "$Hostdisplay" ] && Compositorcommand="$Compositorcommand \\
  --x11-display=$Hostdisplay"
          };;
      esac
    ;;

    --nxagent)
      # files needed by nxagent
      export NXAGENT_KEYSTROKEFILE="$Nxagentkeysfile"
      export NX_CLIENT="$Nxagentclientrc"

      Xserveroptions="$Xserveroptions \\
  -norootlessexit \\
  -ac \\
  -options $Nxagentoptionsfile \\
  -keystrokefile $NXAGENT_KEYSTROKEFILE"
      case "$Desktopmode" in
        "yes") Xserveroptions="$Xserveroptions \\
  -D \\
  -name '${Imagename}_on_${Newdisplay}_(shift+F11_toggles_fullscreen)'" ;;
        "no")  Xserveroptions="$Xserveroptions \\
  -R" ;;
      esac
      Xcommand="$(get_xpath nxagent) :$Newdisplaynumber $Xserveroptions"

      # Some additional nxagent options are stored in a file
      Nxagentoptions="nx/nx"
      [ "$Shareclipboard" = "yes" ] && Nxagentoptions="$Nxagentoptions,clipboard=both" || Nxagentoptions="$Nxagentoptions,clipboard=none"
      case "$Fullscreen" in
        yes) Nxagentoptions="$Nxagentoptions,fullscreen=1" ;;
        no)  [ -n "$Screensize" ] && Nxagentoptions="$Nxagentoptions,geometry=$Screensize" ;;
      esac

      # --composite
      case "$Xcomposite" in
        yes) Nxagentoptions="$Nxagentoptions,composite=1" ;;
        no)  Nxagentoptions="$Nxagentoptions,composite=0" ;;
      esac

      # set keyboard layout
      case "$Xkblayout" in
        "") # set layout from host.
          command -v setxkbmap >/dev/null && {
            Nxagentoptions="$Nxagentoptions,keyboard=$(setxkbmap -query | grep rules | awk '{print $2}')/$(setxkbmap -query | grep layout | awk '{print $2}')"
          } || note "Could not check your keyboard layout due to missing setxkbmap
  If you get mismatching keys, please install setxkbmap.
  $Wikipackages"
        ;;
        *) # --keymap
          case "$Xkblayout" in
            clone) Nxagentoptions="$Nxagentoptions,keyboard='clone'" ;;
            *)     Nxagentoptions="$Nxagentoptions,keyboard='evdev/$Xkblayout'" ;;
          esac
        ;;
      esac

      Nxagentoptions="${Nxagentoptions}:${Newdisplaynumber}"
      echo "$Nxagentoptions" >> "$Nxagentoptionsfile"
      debugnote "$Xserver: Additional nxagent options: $Nxagentoptions"

      # Workaround as nxagent ignores XAUTHORITY and fails to start if option -auth is given without containing the cookie from host display.
      # Option -ac above complies "xhost +" and is reverted in xinitrc.
      [ "$Xauthentication" = "yes" ] && unpriv "cp '$Hostxauthority' '$Xservercookie'"

      # fake NXclient
      echo '#! /usr/bin/env bash
# helper script to terminate nxagent.
# nxagent runs program noted in NX_CLIENT if window close button is pressed.
# (real nxclient does not exist)
echo "NXclient: $*" >> '"$Xinitlogfile"'
parsed="$(getopt --options="" --longoptions="parent:,display:,dialog:,caption:,window:,message:" -- "$@")"
eval set -- $parsed
while [ -n "${1:-}" ] ; do
  case "${1:-}" in
    --dialog) dialog="${2:-}" && shift ;;
    --display|--caption|--message) shift ;;
    --window) shift ;;
    --parent) pid="${2:-}" && shift ;;
    --) ;;
  esac
  shift
done
case $dialog in
  pulldown) ;;
  yesnosuspend)
    kill $pid
    echo timetosaygoodbye >> '"$Timetosaygoodbyefile"'
  ;;
esac
' >> "$NX_CLIENT"
      unpriv "chmod +x '$NX_CLIENT'"

      echo '<!DOCTYPE NXKeystroke>
    <keystrokes>
    <keystroke action="fullscreen" AltMeta="0" Control="0" Shift="1" key="F11" />
    <keystroke action="fullscreen" AltMeta="1" Control="1" Shift="1" key="f" />
</keystrokes>' >> "$NXAGENT_KEYSTROKEFILE"
    ;;

    --xwin)
      case "$Sharegpu" in
        no)
          Iglx="no"
          Xserveroptions="$Xserveroptions \\
  -nowgl" ;;
        yes)
          Iglx="yes"
          Xserveroptions="$Xserveroptions \\
  -wgl" ;;
      esac

      case "$Fullscreen" in
        yes)
          Xserveroptions="$Xserveroptions \\
  -fullscreen" ;;
        no)
          Xserveroptions="$Xserveroptions \\
  -lesspointer"
          case "$Desktopmode" in
            yes)
              for ((Count=0 ; Count<$Outputcount ; Count++)); do
                Xserveroptions="$Xserveroptions \\
  -screen $Count $Screensize"
              done
            ;;
            no) Xserveroptions="$Xserveroptions \\
  -multiwindow" ;;
          esac
        ;;
      esac

      case "$Shareclipboard" in
        yes) Xserveroptions="$Xserveroptions \\
  -clipboard" ;;
        no)  Xserveroptions="$Xserveroptions \\
  -noclipboard" ;;
      esac

      Xcommand="$(command -v XWin) :$Newdisplaynumber $Xserveroptions"
    ;;

    --runx)
      Xserveroptions="--display $Newdisplaynumber \
  --verbose"
      [ "$Xauthentication" = "no" ] && Xserveroptions="$Xserveroptions \
  --no-auth"
      [ "$Desktopmode" = "yes" ]    && Xserveroptions="$Xserveroptions \
  --desktop"
      [ "$Shareclipboard" = "yes" ] && Xserveroptions="$Xserveroptions \
  --clipboard"
      [ "$Screensize" ]             && Xserveroptions="$Xserveroptions \
  --size=$Screensize"
      [ "$Sharegpu" = "yes" ]       && {
        Xserveroptions="$Xserveroptions \
  --gpu"
        store_runoption env "LIBGL_ALWAYS_INDIRECT=1"
      }
      Xcommand="$(command -v runx) $Xserveroptions"
    ;;

    --hostwayland|--hostdisplay|--tty) ;;
  esac

  case "$Xserver" in
    --tty|--hostdisplay|--runx) ;;
    --weston|--kwin|--hostwayland) ;;
    *)
      case "$Iglx" in
        yes) Xcommand="$Xcommand \\
  +iglx"
          store_runoption env "LIBGL_ALWAYS_INDIRECT=1" ;;
        no)
          Xcommand="$Xcommand \\
      -iglx" ;;
      esac
    ;;
  esac

  # --xopt
  Xcommand="$Xcommand \\
  $Xserveroptions_custom"

  case "$Xserver" in
    --weston|--kwin|--hostwayland|--hostdisplay|--tty) Xcommand="" ;;
  esac
  case "$Xserver" in
    --weston|--kwin|--weston-xwayland|--kwin-xwayland|--xpra-xwayland|--xpra2-xwayland) ;;
    *) Compositorcommand="" ;;
  esac

  return 0
}
create_xcontainercommand() {    # option --xc: create docker command for X in container
  local Xcontainerrc Xc_hostx= Xc_hostwayland= Xc_nomitshm Xc_containerwayland Xc_gpu Gpudevice

  Xcontainername="x11docker_X${Newdisplaynumber}_xserver_${Cachenumber}"
  Xcontainerrc="$Cachefolder/xcontainerrc"
  mkfile "$Xcontainerrc"
  mkfile "$Cachefolder/etcpasswd.xcontainer"
  echo "$Containeruser:x:${Containeruseruid:-$Hostuseruid}:${Containerusergid:-$Hostusergid}:$Containeruser,,,:/tmp:/bin/bash" >> "$Cachefolder/etcpasswd.xcontainer"

  # code snippets
  Xc_containerx="\\
  --mount type=bind,source=$Cachefolder/tmp,target=/tmp \\
  --mount type=bind,source=$Xservercookie,target=$Xservercookie \\
  --mount type=bind,source=$Modelinefilebasepath,target=$Modelinefilebasepath,readonly"
  [ -n "$Hostdisplay" ] && {
    Xc_hostx="\\
  --env DISPLAY=$Hostdisplay \\
  --env XAUTHORITY=$Hostxauthority \\
  --mount type=bind,source=$Hostxauthority,target=$Hostxauthority,readonly"
    [ -S "/tmp/.X11-unix/X$Hostdisplaynumber" ] && Xc_hostx="$Xc_hostx \\
  --mount type=bind,source=/tmp/.X11-unix/X$Hostdisplaynumber,target=/X$Hostdisplaynumber,readonly"
  }
  [ -S "$XDG_RUNTIME_DIR/$Hostwaylandsocket" ] && Xc_hostwayland="\\
  --mount type=bind,source=$XDG_RUNTIME_DIR/$Hostwaylandsocket,target=/$Hostwaylandsocket,readonly \\
  --env XDG_RUNTIME_DIR=$XDG_RUNTIME_DIR \\
  --env GDK_BACKEND=wayland \\
  --env WAYLAND_DISPLAY=$Hostwaylandsocket"
  Xc_containerwayland="\\
  --env XDG_RUNTIME_DIR=/run/user/$Containeruseruid \\
  --mount type=bind,source=$Cachefolder/XDG_RUNTIME_DIR,target=/run/user/$Containeruseruid"
  Xc_nomitshm="\\
  --env LD_PRELOAD=/lib/x86_64-linux-gnu/libdl.so.2:/XlibNoSHM.so"
  Xc_gpu="\\
  --group-add $(getent group video | cut -d: -f3) \\
  --group-add $(getent group render | cut -d: -f3)"
  while read -r Gpudevice ; do
    Xc_gpu="$Xc_gpu \\
  --device $Gpudevice:$Gpudevice"
  done < <(setup_gpu_devicelist) ### FIXME nvidia driver
  [ "$Nvidiainstallerfile" ] && {
    Xc_gpu="$Xc_gpu \\
  --mount type=bind,source=$Nvidiainstallerfile,target=$Nvidiacontainerfile,readonly"
    mkfile "$Sharefolder/nvidia_installer"
    rootrc_nvidia_installer >> "$Sharefolder/nvidia_installer"
  }

  # create command
  Xcontainercommand="$Xcontainerbackend run --rm --detach \\
  --tty \\
  --pull=never \\
  --init \\
  --name $Xcontainername \\
  --env HOME=/tmp \\
  --cap-drop ALL \\
  --security-opt=no-new-privileges \\
  --mount type=bind,source=$Sharefolder,target=$Sharefolder \\
  --mount type=bind,source=$Cachefolder/etcpasswd.xcontainer,target=/etc/passwd,readonly \\
  --mount type=bind,source=$Xcontainerrc,target=/xcontainerrc,readonly"
  [ "$Containeruser" = "$Hostuser" ] && {
    Xcontainercommand="$Xcontainercommand \\
  --user ${Containeruseruid}:${Containerusergid}"
  } || {
    Xcontainercommand="$Xcontainercommand \\
  --user ${Hostuseruid}:${Hostusergid}"
  }

  case "$Xoverip" in
    no) Xcontainercommand="$Backendbin \\
  --network=none" ;;
  esac

  case "$Shareipc" in
    host)
      Xcontainercommand="$Xcontainercommand \\
  --ipc=host"
    ;;
    no)
      case "$Xcontainerbackend" in
        docker)
          Xcontainercommand="$Xcontainercommand \\
  --ipc=shareable"
        ;;
      esac
    ;;
  esac

  case "$Xcontainerbackend" in
    podman)
      Xcontainercommand="$Xcontainercommand \\
  --userns=keep-id"
    ;;
  esac

  case "$Runtime" in
    "") ;;
    kata-runtime) ;;
    sysbox-runc)
      case "$Xserver" in
        *xwayland)
          Xcontainercommand="$Xcontainercommand \\
  --runtime runc"
        ;;
        *)
          Xcontainercommand="$Xcontainercommand \\
  --runtime $Runtime"
        ;;
      esac
    ;;
    *)
      Xcontainercommand="$Xcontainercommand \\
  --runtime $Runtime"
    ;;
  esac

  # add code snippets
  case "$Xserver" in
    --xephyr)
      Xcontainercommand="$Xcontainercommand $Xc_containerx"
      Xcontainercommand="$Xcontainercommand $Xc_hostx"
      Xcontainercommand="$Xcontainercommand $Xc_nomitshm"
      Xcontainercommand="$Xcontainercommand $Xc_gpu"
    ;;
    --nxagent)
      Xcontainercommand="$Xcontainercommand $Xc_containerx"
      Xcontainercommand="$Xcontainercommand $Xc_hostx"
      Xcontainercommand="$Xcontainercommand $Xc_nomitshm"
      Xcontainercommand="$Xcontainercommand $Xc_gpu"
    ;;
    --xpra)
      Xcontainercommand="$Xcontainercommand $Xc_containerx"
      [ -n "$Hostdisplay" ] && [ -z "$Hostwaylandsocket" ] && {
        Xcontainercommand="$Xcontainercommand $Xc_hostx"
      }
      [ "$Hostwaylandsocket" ] && {
        Xcontainercommand="$Xcontainercommand $Xc_hostwayland"
      }
      Xcontainercommand="$Xcontainercommand $Xc_nomitshm"
      Xcontainercommand="$Xcontainercommand $Xc_gpu"
    ;;
    --xpra2)
      Xcontainercommand="$Xcontainercommand $Xc_containerx"
      Xcontainercommand="$Xcontainercommand $Xc_gpu"
    ;;
    --xpra-xwayland)
      Xcontainercommand="$Xcontainercommand $Xc_containerx"
      Xcontainercommand="$Xcontainercommand $Xc_hostx"
      Xcontainercommand="$Xcontainercommand $Xc_containerwayland"
      Xcontainercommand="$Xcontainercommand $Xc_gpu"
      Xcontainercommand="$Xcontainercommand $Xc_nomitshm"
    ;;
    --weston-xwayland)
      Xcontainercommand="$Xcontainercommand $Xc_containerx"
      Xcontainercommand="$Xcontainercommand $Xc_hostx"
      Xcontainercommand="$Xcontainercommand $Xc_hostwayland"
      Xcontainercommand="$Xcontainercommand $Xc_containerwayland"
      Xcontainercommand="$Xcontainercommand $Xc_nomitshm"
      Xcontainercommand="$Xcontainercommand $Xc_gpu"
    ;;
    --xpra2-xwayland)
      Xcontainercommand="$Xcontainercommand $Xc_containerx"
      Xcontainercommand="$Xcontainercommand $Xc_hostx"
      Xcontainercommand="$Xcontainercommand $Xc_containerwayland"
      Xcontainercommand="$Xcontainercommand $Xc_gpu"
    ;;
    --xwayland)
      Xcontainercommand="$Xcontainercommand $Xc_containerx"
      Xcontainercommand="$Xcontainercommand $Xc_hostwayland"
      Xcontainercommand="$Xcontainercommand $Xc_gpu"
    ;;
    --weston)
      Xcontainercommand="$Xcontainercommand $Xc_hostx"
      Xcontainercommand="$Xcontainercommand $Xc_hostwayland"
      Xcontainercommand="$Xcontainercommand $Xc_containerwayland"
      Xcontainercommand="$Xcontainercommand $Xc_nomitshm"
      Xcontainercommand="$Xcontainercommand $Xc_gpu"
    ;;
    --kwin)
      Xcontainercommand="$Xcontainercommand $Xc_hostx"
      Xcontainercommand="$Xcontainercommand $Xc_hostwayland"
      Xcontainercommand="$Xcontainercommand $Xc_containerwayland"
      Xcontainercommand="$Xcontainercommand $Xc_nomitshm"
      Xcontainercommand="$Xcontainercommand $Xc_gpu"
      Xcontainercommand="$Xcontainercommand \\
  --cap-add SYS_RESOURCE"
    ;;
  esac
  Xcontainercommand="$Xcontainercommand \\
  $Xcontainerimage bash /xcontainerrc"
  
  echo "#! /bin/bash
# script running in X server container (option --xc)
exec >> $Xinitlogfile 2>&1
$(declare -f rocknroll)
Timetosaygoodbyefile=$Timetosaygoodbyefile

mkdir -p -m 1777 /tmp/.X11-unix
[ -S /X${Hostdisplaynumber} ] && ln -v -s /X$Hostdisplaynumber /tmp/.X11-unix/X$Hostdisplaynumber
[ -S /${Hostwaylandsocket} ]  && ln -v -s /$Hostwaylandsocket \$XDG_RUNTIME_DIR/$Hostwaylandsocket
" >> "$Xcontainerrc"

  [ "$Virgl" = "yes" ] && echo "
virgl_test_server &
" >> "$Xcontainerrc"

  echo "
# wait for the end
case $Usemkfifo in
  yes) read -n1 goodbye <$Timetosaygoodbyefifo ;;
  no)  while rocknroll; do sleep 1; done ;;
esac
exit 0
" >> "$Xcontainerrc"

  return 0
}
create_xinitrc() {              # create xinitrc: set up X environment, create cookies
  echo "#! /bin/sh"

  declare -f disable_xhost
  declare -f pspid
  declare -f rocknroll
  declare -f storeinfo
  echo "$Messagefifofuncs"
  [ "$Xcontainer" = "yes" ] && {
    echo "storepid() { : ; }"
  } || {
    declare -f storepid
  }

  echo               "getscreensize() {"
  echo               "  CurrentXaxis=\"\$(xrandr | grep primary | cut -d' ' -f4 | cut -dx -f1 )\""
  echo               "  CurrentYaxis=\"\$(xrandr | grep primary | cut -d' ' -f4 | cut -dx -f2 | cut -d+ -f1)\""
  echo               "}"
  echo               "checkscreensize() {"
  echo               "  getscreensize"
  echo               "  [ \"\$Xaxis\" = \"\$CurrentXaxis\" ] || return 1"
  echo               "  [ \"\$Yaxis\" = \"\$CurrentYaxis\" ] || return 1"
  echo               "  return 0"
  echo               "}"
  echo               "getprimary() {"
  echo               "  xrandr | grep -q primary || xrandr --output \$(xrandr | grep ' connected' | head -n1 | cut -d' ' -f1) --primary"
  echo               "  echo \$(xrandr | grep primary | cut -d' ' -f1)"
  echo               "}"
  echo               ""

  echo               "Messagefile='$Messagefifo'"
  echo               "Output=\"\$(getprimary)\""
  echo               "Storeinfofile='$Storeinfofile'"
  echo               "Storepidfile='$Storepidfile'"
  echo               "Timetosaygoodbyefile='$Timetosaygoodbyefile'"
  echo               ""
  echo               "export PATH='${PATH:-}'"
  echo               ""
  echo               "Cookie=''"
  echo               "Line=''"
  echo               "Var=''"
  echo               ""

  echo               "debugnote 'Running xinitrc'"
  echo               ""

  case "$Xserver" in
    --weston|--kwin|--hostwayland)
      echo           "export $Newxenv"
      echo           "unset  DISPLAY XAUTHORITY"
      echo           "export DISPLAY XAUTHORITY"
    ;;
    --tty)
      echo           "unset  DISPLAY XAUTHORITY WAYLAND_DISPLAY"
      echo           "export DISPLAY XAUTHORITY WAYLAND_DISPLAY"
    ;;
    --runx)
      [ "$Xauthentication" = "yes" ] && {
        echo         "# cookie generated by runx"
        echo         'debugnote "xinitrc: Option --runx: Using cookie: $XAUTHORITY"'
        echo         "cp -T \"\$XAUTHORITY\" '$Xclientcookie'"
        echo         "cp -T \"\$XAUTHORITY\" '$Xservercookie'"
        echo         "debugnote \"xinitrc: Cookie: \$(xauth -f $Xclientcookie list 2>&1)\""
      }
      echo           "export $Newxenv"
    ;;
    *) # here something for real X servers
      echo           "export $Newxenv"
      echo           "# background color"
      case "$Xserver" in
        --hostdisplay) ;;
        --nxagent)
          echo       "sleep 2 && xsetroot -solid '#7F7F7F' 2>/dev/null &" ;;
        *) echo      "xsetroot -solid '#7F7F7F' 2>/dev/null" ;;
      esac
      echo ""

      [ "$Xauthentication" = "yes" ] && {
        echo         "# create new XAUTHORITY cookies"
        echo         ":> $Xclientcookie"
        echo         ""
        [ "$Xserver" = "--nxagent" ] && {
          echo       "cp $Xclientcookie $Xclientcookie.bak   # nxagent workaround cookie was created before starting xinit"
          echo       "export XAUTHORITY=$Xclientcookie.bak"
          echo       ""
        }
        [ "$Trusted" = "yes" ] && Trusted="trusted" || Trusted="untrusted"
        [ "$Xserver" = "--hostdisplay" ] && {
          [ -s "$Hostxauthority" ] && echo "export XAUTHORITY=$Hostxauthority"
          echo       "xhost | grep -q 'SI:localuser:$Hostuser' || { xhost +SI:localuser:$Hostuser ; Xhostentry='yes' ; }"
          echo       ""
        }
        case "$Runsoverssh" in
          no)
            echo     "echo 'Requesting $Trusted cookie from X server'"
            echo     "xauth -v -i -f $Xclientcookie generate $Newdisplay . $Trusted timeout 3600"
          ;;
          yes)
            echo     "verbose 'Can not use cookies created over SSH. Will bake one myself.'"
          ;;
        esac
        echo         ""
        echo         "[ -s '$Xclientcookie' ] || { "
        echo         "  [ '$Trusted' = 'untrusted' ] && note 'Could not create untrusted cookie.
  Maybe your X server misses extension SECURITY.'"
        [ "$Xserver" = "--hostdisplay" ] && {
          [ "$Shareipc" != "host" ] && [ "$Runsoverssh" = "no" ] && [ "$Hostmitshm" = "yes" ] && [ "$Xtoolscontainer" = "no" ] && {
            echo     "  warning 'Option --hostdisplay: Memory access failures and rendering glitches
  may occur due to unrestricted cookie.
  Avoid them with isolation breaking option --ipc=host,
  or use another X server option like --nxagent or --xpra.'"
          }
          echo       "  [ '$Trusted' = 'untrusted' ] && warning 'SECURITY RISK! Keylogging and remote host control "
          echo       "  may be possible! Better avoid using option --hostdisplay,"
          echo       "  rather use --nxagent or --xpra.'"
          echo       "  cp $Hostxauthority $Xclientcookie"
        }
        echo         "}"
        echo         "[ -s '$Xclientcookie' ] || { "
        echo         "  # still no cookie? try to create one without extension security"
        echo         "  debugnote 'xinitrc: Failed to retrieve trusted cookie from X server. Will bake one myself.'"
        echo         "  echo 'Failed to retrieve trusted cookie from X server. Will bake one myself.'"
        echo         "  xauth -v -i -f $Xclientcookie add :$Newdisplaynumber . $(makecookie)"
        echo         "  ls -l $Xclientcookie"
        echo         "}"
        echo         ""
        echo         "# Prepare cookie with localhost identification disabled by ffff, needed if X socket is shared. ffff means 'familiy wild'"
        echo         "Cookie=\"\$(xauth -i -f $Xclientcookie nlist | sed -e 's/^..../ffff/')\""
        echo         "echo \"\$Cookie\" | xauth -v -i -f $Xclientcookie nmerge -"
        echo         ""
        echo         "debugnote \"xinitrc: Created cookie: \$(xauth -f $Xclientcookie list 2>&1)\""
        echo         "ls -l $Xclientcookie"
        echo         "truncate -s0 $Xservercookie"
        echo         "cat $Xclientcookie >> $Xservercookie"
#        echo         "cp $Xclientcookie $Xservercookie"
        echo         "chmod 644 $Xclientcookie"
        echo         ""
        echo         "[ -s '$Xclientcookie' ] || warning 'Cookie creation failed!'"
        [ "$Xserver" = "--hostdisplay" ] && echo "[ '\$Xhostentry' = 'yes' ] && env XAUTHORITY=$Hostxauthority xhost -SI:localuser:$Hostuser"
        [ "$Xserver" = "--nxagent" ]     && echo "rm $Xclientcookie.bak"
      }
      echo           "export XAUTHORITY=$Xclientcookie"
      echo           "[ '$Xauthentication' = 'no' ] || [ ! -s '$Xclientcookie' ] && unset XAUTHORITY && warning '$Xserver: X server $Newdisplay runs without cookie authentication.'"
        echo         ""

      case "$Xserver" in
        --hostdisplay) ;;   # do not change host settings
        --xwin) ;;          # xhost does not work over tcp (?)
        *)
          case "$Xauthentication" in
            yes)
              echo   "# clean xhost"
              echo   "verbose 'Disabling any possible access to new X server possibly granted by xhost'"
              echo   "disable_xhost"
            ;;
          esac
          [ -n "$Xhost" ] && {
            [ "$Xhost" = "auto" ] && Xhost="+SI:localuser:$Containeruser"
            echo     "warning \"Option --xhost: Running 'xhost $Xhost' on $Newdisplay\""
            echo     "xhost $Xhost"
          }
          echo       ""
        ;;
      esac

      case "$Xserver" in
        --hostdisplay|--xwin|--nxagent) ;;
        --hostwayland|--weston|--kwin|--tty) ;;
        *)
          echo       "# Keyboard layout"
          command -v setxkbmap >/dev/null && {
            case "$Xkblayout" in
              "") [ -n "$Hostdisplay" ] && setxkbmap -display "$Hostdisplay" -print >> "$Xkbkeymapfile" ;;
              *)  setxkbmap "$Xkblayout" -print >> "$Xkbkeymapfile" ;;
            esac
            :
          } || {
            note "setxkbmap not found. Need setxkbmap and xkbcomp to set keyboard layout.
  $Wikipackagesimage"
          }
          [ -s "$Xkbkeymapfile" ] && {
            echo     "# set keyboard layout on $Newdisplay"
            echo     "verbose \"Keyboard layout:"
            echo     "\$(cat $Xkbkeymapfile)\""
            echo     "xkbcomp $Xkbkeymapfile $Newdisplay"
          }
          echo       ""
        ;;
      esac
    ;;
  esac

  case "$Xserver" in
    --xpra|--xpra2|--xvfb)
      echo           "# create set of different screen resolutions"
      echo           "xrandr --newmode $Modeline"
      echo           "xrandr --addmode \$Output $(echo $Modeline | cut -d " " -f1)"
      echo           "while read Line; do"
      echo           "  Line=\"\$(echo \"\$Line\" | sed 's/Modeline//g')\""
      echo           "  Line=\"\$(echo \"\$Line\" | sed 's/\"//g')\""
      echo           "  xrandr --newmode \$Line 2>/dev/null"
      echo           "  xrandr --addmode \"\$Output\" \$(echo \$Line | cut -d' ' -f1) 2>/dev/null"
      echo           "done < \"$Modelinefile\""
      [ "$Xserver" != "--xvfb" ] && [ "$Desktopmode" = "yes" ] && echo "xrandr --output  \$Output --mode $(echo $Modeline | cut -d " " -f1)"
      echo           ""
    ;;
    --xorg) # --xorg: --scale, --size, --rotate
      echo           "# determine screen size"
      echo           '[ -n "$(xrandr | grep connected | grep -v disconnected)" ] && {'
      [ -z "$Screensize" ] && {
        echo         "  getscreensize"
        echo         "  Xaxis=\"\$CurrentXaxis\""
        echo         "  Yaxis=\"\$CurrentYaxis\""
        [ "$Scaling" ] && echo "  Xaxis=\"\$(awk -v a=\"\$Xaxis\" -v b=\"$Scaling\" 'BEGIN {print (a / b)}')\""
        echo         "  Xaxis=\"\${Xaxis%.*}\""
        [ "$Scaling" ] && echo "  Yaxis=\"\$(awk -v a=\"\$Yaxis\" -v b=\"$Scaling\" 'BEGIN {print (a / b)}')\""
        echo         "  Yaxis=\"\${Yaxis%.*}\""
      } || {
        echo         "  Xaxis='$Xaxis'"
        echo         "  Yaxis='$Yaxis'"
      }
      echo           "  Screensize=\"\${Xaxis}x\${Yaxis}\""
      echo           ""

      [ "$Screensize" ] && [ -z "$Scaling" ] && {
        echo         "  # Switch to desired screen size $Screensize"
        echo         "  [ -n \"\$(xrandr | grep \$Screensize)\" ] && { "
        echo         "    note \"Will try to set native resolution \$Screensize."
        echo         "  If that looks ugly, use --scale=1 to enforce a fake scaled resolution.\""
        echo         "    xrandr --output \$Output --mode \$Screensize"
        echo         "  } || note \"Resolution \$Screensize not found in xrandr.\""
        echo         ""
      }

      [ "$Screensize" ] && [ -z "$Scaling" ] && {
        echo         "  checkscreensize || {"
        echo         "    note \"Panning \$Screensize. If virtual screen is greater than "
        echo         "  maximal screen size, you can move virtual screen with mouse at screen edges."
        echo         "  You can force the virtual screen to match your monitor with option --scale=1\""
        echo         "    xrandr --output \$Output --panning \$Screensize+0+0/\$Screensize+0+0/100/100/100/100 --verbose"
        echo         '  }'
        echo         "  checkscreensize || {"
        echo         "    note 'Panning failed, trying to scale instead.'"
        echo         "    xrandr --output \$Output --scale-from \$Screensize --panning \$Screensize+0+0/\$Screensize+0+0"
        echo         "    checkscreensize && note \"Successfully set screen size \$Screensize\""
        echo         '  }'
        echo         "  checkscreensize || {"
        echo         "    getscreensize"
        echo         "    note \"Setting desired resolution \$Screensize failed."
        echo         "  Fallback: Will use detected \${CurrentXaxis}x\${CurrentYaxis} instead.\""
        echo         '  }'
        echo         ""
      }

      [ "$Scaling" ] && {
        echo         "  # --scale $Scaling"
        [ "$Screensize" ] && [ "$Scaling" != "1" ] && echo "  note 'Cannot set panning and scaling at the same time.
  Desired screen size $Screensize will be scaled to your monitor size
  for arbitrary values you may provide with option --scale.'"
        echo         "  # Scaling $Scaling"
        echo         "  note \"Setting scaled resolution \$Screensize\" with scale factor $Scaling."
        # must use --scale-from and --panning because --scale causes mouse barriers/crtc-boundaries
        echo         "  xrandr --output \$Output --scale-from \$Screensize --panning \$Screensize+0+0/\$Screensize+0+0 --verbose"
        echo         "  checkscreensize || {"
        echo         "    getscreensize"
        echo         "    note \"Setting desired resolution \$Screensize failed."
        echo         "  Detected resolution \${CurrentXaxis}x\${CurrentYaxis} instead.\""
        echo         "  }"
        echo         ""
      }

      [ -n "$Rotation" ] && {
        echo         "  # --rotate $Rotation"
        echo         "  verbose 'Rotation $Rotation'"
        case "$Rotation" in
          0|normal)    Rotation="" ;;
          90)          Rotation="--rotate right";;
          180)         Rotation="--reflect xy" ;;
          270)         Rotation="--rotate left";;
          flipped)     Rotation="--reflect y";;
          flipped-90)  Rotation="--rotate right --reflect x";;
          flipped-180) Rotation="--reflect x";;
          flipped-270) Rotation="--rotate left --reflect x";;
        esac
        echo         "  bash -c 'while read Line ; do xrandr --output \$Line $Rotation ; done < <(xrandr | grep \" connected\" | cut -d \" \" -f1)'"
        echo         ""
      }
      echo           "  :"
      echo           "} || {"
      echo           "  Xaxis=${Xaxis:-1024}"
      echo           "  Yaxis=${Yaxis:-768}"
      echo           "  Screensize=\"\${Xaxis}x\${Yaxis}\""
      echo           "  note \"Could not detect any connected monitor."
      echo           "  Running on a server? Will try to set a framebuffer size"
      echo           "  with \"xrandr --fb \$Screensize\" that may serve as a virtual display.\""
      echo           "  xrandr --fb \$Screensize"
      echo           "}"
      echo           ""
    ;;
  esac

  [ -n "$Newdisplay" ] && echo "verbose \"Output of xrandr on $Newdisplay
\$(xrandr)\""
  echo ""

  # --wm
  [ "$Windowmanagermode" = "host" ] && {
    echo             "# window manager"
    echo             "$Windowmanagercommand & storepid \$! windowmanager"
    echo             "# only one desktop"
    echo             "sleep 1 && wmctrl -n 1 &"
  }

  # --xfishtank
  [ "$Xfishtank" = "yes" ] && echo "xfishtank & storepid \$! xfishtank"

  echo               "echo 'xinitrc: xinitrc is ready'"
  echo               "storeinfo xinitrc=ready"
  echo               ""

  [ "$Shareclipboard" = "yes" ] && [ -n "$Hostdisplay" ] && {
    case "$Xserver" in
      --xpra*|--nxagent|--xwin) ;;           # have their own clipboard management
      --hostdisplay) ;;                                     # already same clipboard
      *)                                                    # synchronizing between different X servers
        echo         "# option '-c, --clipboard': Run clipboard script "
        echo         "# (text copy only) (xpra has its own clipboard management including images)"
        echo         "bash $Clipboardrc"
        echo         ""
      ;;
    esac
  }

  echo               "# wait for the end"
  case "$Usemkfifo" in
    yes) echo        "read Var <$Timetosaygoodbyefifo" ;;
    no)  echo        "while rocknroll; do sleep 1; done" ;;
  esac

  return 0
}
disable_xhost() {               # remove any access to X server granted by xhost
  local Line=
  command -v xhost >/dev/null || {
    warning "Command 'xhost' not found.
  Can not check for possibly allowed network access to X.
  Please install 'xhost'.
$Wikipackages"
    return 1
  }
  xhost 2>&1 | tail -n +2  /dev/stdin | while read -r Line ; do  # read all but the first line (header)
    debugnote "xhost: Removing entry $Line"
    xhost -"$Line"                                # disable every entry
  done
  xhost -                                         # enable access control
  [ "$(xhost 2>&1 | wc -l)" -gt "1" ] && {
    warning "Remaining xhost permissions found on display ${DISPLAY:-}
$(xhost 2>&1 )"
    return 1
  }
  xhost 2>&1 | grep "access control disabled" && {
    warning "Failed to restrict xhost permissions.
  Access to display ${DISPLAY:-} is allowed for everyone."
    return 1
  }
  return 0
}
store_newxenv() {               # store display variables
  Newdisplay="${Newdisplay//"XCONTAINERIP"/"$Xcontainerip"}"
  # create $Newxenv: collection of environment variables to access new X from host (e.g. in xinitrc)
  [ "$Newdisplay" ]              && storeinfo "DISPLAY=$Newdisplay"                && Newxenv="$Newxenv
DISPLAY=$Newdisplay"
  [ "$Xauthentication" = "yes" ] && storeinfo "XAUTHORITY=$Xclientcookie"          && Newxenv="$Newxenv
XAUTHORITY=$Xclientcookie"
  [ "$Newxsocket" ]              && storeinfo "XSOCKET=$Newxsocket"                && Newxenv="$Newxenv
XSOCKET=$Newxsocket"
  [ "$Newwaylandsocket" ]        && storeinfo "WAYLAND_DISPLAY=$Newwaylandsocket"  && Newxenv="$Newxenv
WAYLAND_DISPLAY=$Newwaylandsocket"
  [ "$Setupwayland" = "yes" ] && for Line in $Waylandtoolkitenv ; do                  Newxenv="$Newxenv
$Line" ; done
  [ -n "$XDG_RUNTIME_DIR" ] && storeinfo "XDG_RUNTIME_DIR=$XDG_RUNTIME_DIR"        && Newxenv="$Newxenv
XDG_RUNTIME_DIR=$XDG_RUNTIME_DIR"
  Newxenv_cr="$(grep . <<< "$Newxenv")"
  Newxenv="$(echo $Newxenv)"
  storeinfo "Xenv=$Newxenv"

  # X / Wayland environment variables for container
  case "$Xserver" in
    --xpra*|--xephyr|--weston-xwayland|--hostdisplay|--xorg|--xvfb|--xwayland|--kwin-xwayland|--nxagent|--xwin|--runx)
      store_runoption env "DISPLAY=$Newdisplay"
      [ "$Xauthentication" = "yes" ] && store_runoption env "XAUTHORITY=$(convertpath share "$Xclientcookie")"
    ;;
    --weston|--kwin|--hostwayland|--tty)
      store_runoption env "WAYLAND_DISPLAY=$Newwaylandsocket"
    ;;
  esac
  [ "$Setupwayland" = "yes" ] && {
    for Line in $Waylandtoolkitenv; do
      store_runoption env "$Line"
    done
  }
  return 0
}
weston_getoutputname() {        # short startup of weston on tty to grep output name
  unpriv "$Weston --no-config --backend=drm-backend.so >> '$Compositorlogfile' 2>&1 & echo compositorpid=\$! >> '$Storeinfofile'"
  waitforlogentry "weston-screencheck" "$Compositorlogfile" "connector" "$Compositorerrorcodes"

  grep Output < "$Compositorlogfile" | grep connector | head -n1 | cut -d ' ' -f3 | rev | cut -c2- | rev

  termpid "$(storeinfo dump compositorpid)" weston
  storeinfo drop compositorpid
  mkfile "$Compositorlogfile"
}

#### backend command setup
check_image() {                 # get some image information

  # Check if image $Imagename is available locally
  Imageinspect="$($Backendbin inspect "$Imagename" 2>> "$Containerlogfile")" || error "Image $Imagename not found locally.
  Please pull or build image first."

  # Check architecture
  Imagearchitecture=$(parse_inspect "$Imageinspect" "Architecture")
  debugnote "Image architecture: $Imagearchitecture"

  # Check CMD
  Imagecommand="$(parse_inspect "$Imageinspect" "Config" "Cmd")"
  debugnote "Image CMD: $Imagecommand"
  [ -z "$Containercommand" ] && {
    Containercommand="$Imagecommand"
    grep -q "$(convertpath share "$Containerrc")" <<< "$Imagecommand" && error "Recursion error: Found CMD $Imagecommand in image.
  Did you use 'docker commit' with an x11docker container?
  Please build new images with a Dockerfile instead of using docker commit,
  or provide a different container command in x11docker command."
  }

  # Check USER
  Imageuser="$(parse_inspect "$Imageinspect" "Config" "User")"
  debugnote "Image USER: $Imageuser"
  case "$Createcontaineruser" in
    yes)
      [ "$Imageuser" ] && note "Found 'USER $Imageuser' in image.
  If you want to run with user $Imageuser instead of host user $Containeruser,
  than run with option --user=RETAIN."
    ;;
    no)
      Containeruser="${Imageuser:-root}"
    ;;
  esac
  storeinfo containeruser="$Containeruser"

  # Check ENTRYPOINT
  Imageentrypoint="$(parse_inspect "$Imageinspect" "Config" "Entrypoint")"
  debugnote "Image ENTRYPOINT: $Imageentrypoint"
  case "$Noentrypoint" in
    yes) Containerentrypoint="" ;;
    no)
      Containerentrypoint="$Imageentrypoint"
      case "$Initsystem" in
        systemd|sysvinit|runit|openrc|tini)
          grep -qE 'tini|init|systemd' <<< "$Containerentrypoint" && {
            note "There seems to be an init system in ENTRYPOINT of image:
    $Containerentrypoint
  Will disable it as x11docker already runs an init with option --$Initsystem.
  To allow this ENTRYPOINT, run x11docker with option --init=none."
            Containerentrypoint=""
          }
        ;;
        s6-overlay)
          [ "$Containerentrypoint" = '/init' ] && {
            Containerentrypoint=""
            [ "$Containercommand" ] || Containercommand="sh -c 'while :; do sleep 10; done'"
          }
        ;;
        none)
          grep -qE 'tini|init|systemd' <<< "$Containerentrypoint" && {
            note "There seems to be an init system in ENTRYPOINT of image:
  $Containerentrypoint
  Returning correct exit code of container command will likely fail."
            Cmdexec="exec"
          }
        ;;
      esac
    ;;
  esac

  # Check WORKDIR
  Imageworkdir="$(parse_inspect "$Imageinspect" "Config" "Workdir")"
  debugnote "Image WORKDIR: $Workdir"
  [ -z "$Workdir" ] && [ -n "$Imageworkdir" ] && {
    note "Found 'WORKDIR $Imageworkdir' in image.
  You can change it with option --workdir=DIR."
    Workdir="$Imageworkdir"
  }

  [ -z "$Containercommand$Containerentrypoint" ] && error 'No container command specified and no CMD or ENTRYPOINT found in image.'

  return 0
}
check_runtime() {               # option --runtime
  local Defaultruntime

  case "$Backend" in
    docker)  Defaultruntime="$(parse_inspect "$Backendinfo" "DefaultRuntime" | tr -d '"')" ;;
    podman)  Defaultruntime="$(parse_inspect "$Backendinfo" "host"  "ociRuntime" "name" | tr -d '"')" ;;
    nerdctl) Defaultruntime="" ;;
  esac

  debugnote "Default runtime: $Defaultruntime"
  [ -z "$Runtime" ]        && Runtime="$Defaultruntime"

  return 0
}
check_cgroup() {                # check [and create] cgroup mountpoint for systemd or elogind
  local Remounted Needcgroup

  [ "$Initsystem" = "systemd" ] && Needcgroup="systemd" || Needcgroup="elogind"

  [ "$Backend" = "docker" ] && {
    case "$Cgroupversion" in
      v1) 
        Sharecgroup="yes"
      ;;
      v2)
        case "$Initsystem" in
          systemd)
            case "$Runtime" in
              sysbox-runc) ;;
              *)
                Remountcgroup="yes"
                warning "Option --init=systemd: To support systemd in docker container
  with cgroupv2 on host, x11docker shortly runs a privileged container
  of '$Imagename' with 'nsenter' to remount the container's /sys/fs/cgroup.
  Evil images might be set up to abuse these privileges."
              ;;
            esac
          ;;
        esac
      ;;
    esac
    case "$Initsystem" in
      sysvinit|openrc|runit)
        Sharecgroup="yes"
      ;;
    esac
  }

  [ "$Sharecgroup" = "yes" ] && warning "Option --init=$Initsystem: Sharing /sys/fs/cgroup from host."

  findmnt "/sys/fs/cgroup/$Needcgroup" >/dev/null && return 0

  case "$Initsystem" in
    systemd)
      case "$Cgroupversion" in
        v1) 
          [ "$Startuser" != "root" ] && note "Option --init=$Initsystem: Did not find /sys/fs/cgroup/$Needcgroup
  Startup of container is likely to fail."
        ;;
        v2)
          Needcgroup=""
        ;;
      esac
    ;;
    *)
      note "Option --init=$Initsystem: Did not find /sys/fs/cgroup/$Needcgroup
  A possible elogind service in container is likely to fail."
    ;;
  esac
  
  [ -z "$Needcgroup" ] && return 0

  [ "$Startuser" != "root" ] && {
    note "Option --init=$Initsystem: To create and mount a cgroup
  for $Needcgroup, please run x11docker as root.
  Or create cgroup mountpoint on host yourself with:
    mount -o remount,rw cgroup /sys/fs/cgroup
    mkdir -p /sys/fs/cgroup/$Needcgroup
    mount -t cgroup cgroup /sys/fs/cgroup/$Needcgroup -o none,name=$Needcgroup"
    return 1
  }

  [ "$Sharecgroup" = "yes" ] && [ "$Startuser" = "root" ] && {
    note "Option --init=$Initsystem: Creating cgroup mountpoint on host for '$Needcgroup'."
    findmnt /sys/fs/cgroup -O ro >/dev/null && {
      mount -o remount,rw cgroup /sys/fs/cgroup >> "$Containerlogfile" 2>&1
      Remounted=yes
    }
    mkdir -p /sys/fs/cgroup/$Needcgroup >> "$Containerlogfile" 2>&1
    mount -t cgroup cgroup /sys/fs/cgroup/$Needcgroup -o none,name=$Needcgroup  >> "$Containerlogfile" 2>&1
    [ "${Remounted:-}" = "yes" ] && {
      mount -o remount,ro cgroup /sys/fs/cgroup  >> "$Containerlogfile" 2>&1
    }
  }
  
  return 0
}
check_containerhomebasedir() {  # options --homebasedir, --home
  ## option '--homebasedir': Specify base folder here to store container home folders for --home
  case "$Backend" in
    proot|chroot)
      Hosthomebasefolder="$Localsharedir"
    ;;
    docker|podman|nerdctl)
      [ "$Hosthomebasefolder" ] || case "$Mobyvm" in
        no)  Hosthomebasefolder="$Containeruserhosthome/.local/share/x11docker" ;;
        yes) Hosthomebasefolder="$(convertpath subsystem "$(wincmd 'echo %userprofile%') ")/x11docker/home" ;;
      esac
    ;;
  esac
}
check_containerhome() {         # option --home: check HOME of container user.
  ## option '--home':        Share folder ~/.local/share/x11docker/imagename with created container as its home directory
  ## option '--home=DIR':    Share custom host folder as home

  # rootless with userns-remap
  [ "$Rootlessbackend" ] && {
    case "$Backend" in
      docker|nerdctl)
        [ "$Sharehome" != "no" ] && {
          note "Option --home is not supported in $Backend rootless mode.
  In rootless mode only option --backend=podman supports option --home.
  Alternatively run one of docker, podman or nerdctl in rootful mode.
  Fallback: Disabling option --home"
          check_fallback
          Sharehome="no"
        }
      ;;
    esac
  }

  case "$Sharehome" in
    yes|host)
      [ -z "$Persistanthomevolume" ] && Persistanthomevolume="$Hosthomebasefolder/$Imagebasename"
      Persistanthomevolume="${Persistanthomevolume//"~"/"$Hostuserhome"}"
      [ "${Persistanthomevolume:0:1}" = "/" ] && Sharehome="host" || Sharehome="volume"
    ;;
  esac

  case "$Sharehome" in
    host)
      case "$Createcontaineruser" in
        no)
          note "Option --home or --home=DIR is not supported
  with option --user=RETAIN.
  Alternatively, specify a docker volume with --home=VOLUME.
  Also you can use option --share to share host directories.
  Fallback: Disabling option --home."
          check_fallback
          Sharehome="no"
        ;;
        yes)
          grep -q "unknown" <<< "$Containeruser" && {
            note "Option --home: Sharing a host folder is allowed only
  for container users that also exist on host.
  You can use a docker volume with --home=VOLUME instead.
  Fallback: Disabling option --home."
            check_fallback
            Sharehome="no"
          }
        ;;
      esac
    ;;
  esac

  case "$Sharehome" in
    host)
      Containeruserhomebasefolder="/home"
      [ "$Containeruser" = "root" ] && Containeruserhomebasefolder="/"
      # A change can break existing configs, e.g. playonlinux
#      Containeruserhomebasefolder="/home.x11docker"
      [ "$Persistanthomevolume" = "$Containeruserhosthome" ] && {
        # --home=$HOME must be same as on host #243
        Containeruserhomebasefolder="$(dirname "$Containeruserhosthome")"
        Containeruserhome="$Containeruserhosthome"
      }
    ;;
    no)
#      Containeruserhomebasefolder="/home.tmp"
      Containeruserhomebasefolder="/home"
      [ "$Containeruser" = "root" ] && Containeruserhomebasefolder="/"
    ;;
    volume)
      Containeruserhomebasefolder="/home.volume/$Persistanthomevolume"
      grep -q "/" <<< "$Persistanthomevolume" && error "Option --home: Invalid argument: '$Persistanthomevolume'
  Please either specify an absolute path beginning with '/'
  or specify a docker volume without any '/'."
    ;;
  esac
  [ "$Createcontaineruser" = "yes" ] && Containeruserhome="${Containeruserhome:-$Containeruserhomebasefolder/$Containeruser}"
  [ "$Sharehome" != "no" ] && store_runoption env "HOME=$Containeruserhome"

#  case "$Createcontaineruser" in
#    no)  store_runoption env "HOME=/tmp" ;;
#  esac

  case "$Sharehome" in
    host)
      # if no home folder on host is specified (--home=DIR), create a standard one in ~/.local/share/x11docker
      [ -d "$Persistanthomevolume" ] || {
        [ "$Startuser" = "root" ] && su "$Containeruser" -c "mkdir -p '$Persistanthomevolume'"
        [ "$Containeruser" = "$Hostuser" ] && unpriv "mkdir -p '$Persistanthomevolume'" && {
          # create symbolic link to ~/x11docker
          echo "$Persistanthomevolume" | grep -q .local/share/x11docker && [ ! -e "$Hostuserhome/x11docker" ] && unpriv "ln -s '$Hosthomebasefolder' '$Hostuserhome/x11docker'" ||:
        }
      }
      [ -d "$Persistanthomevolume" ] || error "Option --home: Could not create persistent home folder for
  user '$Containeruser' on host. Can e.g. happen with option --user.
  Four possibilities to solve issue:
  1.) Run x11docker one time as user '$Containeruser'.
  2.) Run x11docker one time as user 'root'.
  3.) Use option --home=DIR with DIR pointing to a writeable folder.
  4.) Use option --home=VOLUME to use a docker volume."
      writeaccess "$Containeruseruid" "$Persistanthomevolume" || warning "User '$Containeruser' might have no write access to
  $Persistanthomevolume."
      verbose "Sharing directory $Persistanthomevolume
  with container as its home directory $Containeruserhome"
    ;;
    volume)
      debugnote "Option --home: Using docker volume $Persistanthomevolume"
    ;;
  esac

  return 0
}
check_containeruser() {         # check container user (also option --user)
  ## check container user
  [ "$Containeruser" = "RETAIN" ] && {
    Createcontaineruser="no"
    Containeruser=""
    return 0
  }

  [ -z "$Containeruser" ] && Containeruser="$Hostuser"               # default: containeruser = hostuser. can be changed with --user
  [ -n "$Containeruser" ] && echo "$Containeruser" | grep -q ':' && {  # option --user can specify a group/gid after :
    Containerusergid="$(echo "$Containeruser" | cut -d: -f2)"
    Containeruser="$(echo "$Containeruser" | cut -d: -f1)"
  }
  [ "$Containeruser" = "root" ] && Containeruser="0"
  [ -n "$(getent passwd "$Containeruser")" ] && {                      # user exists on host
    Containeruser=$(getent passwd "$Containeruser" | cut -d: -f1)      # can be name or uid -> now name
    Containeruseruid=$(getent passwd "$Containeruser" | cut -d: -f3)
    [ -z "$Containerusergid" ] && Containerusergid="$(getent passwd "$Containeruser" | cut -d: -f4)"
    [ "$Containeruser" = "$Hostuser" ] && Containeruserhosthome="$Hostuserhome"
    [ -z "$Containeruserhosthome" ]    && Containeruserhosthome="$(getent passwd "$Containeruser" | cut -d: -f6)"
    :
  } || {                                                   # user does not exist on host
    [[ "$Containeruser" =~ ^[0-9]+$ ]] || error "Option --user: Unknown host user or invalid user number '$Containeruser'.
  Non-host users can be specified with an UID only, not with a name."
    Containeruseruid="$Containeruser"
    Containeruser="unknown$Containeruseruid"
    [ -z "$Containerusergid" ] && Containerusergid=100
    Containeruserhosthome=""
  }

  Containerusergroup="$(getent group "$Containerusergid" | cut -d: -f1 || echo group_"$Containeruser")"
  [ "$Containeruseruid" = "0" ] && {
    Containeruser="root"
    Containerusergid="0"
    Containerusergroup="root"
    Containeruserhosthome="/root"
    [ "$Sudouser" = "yes" ] || [ "$Capdropall" = "no" ] || {
      note "Option --user=root: Please set option --sudouser or --cap-default
  if you want root privileges for container user root."
    }
  }

  [ -f "$Passwordfile" ] && {
    verbose "Found password file $Passwordfile"
    Containeruserpassword="$(cat "$Passwordfile")"
    case "$(stat -c '%a' "$Passwordfile")" in
      600|400) ;;
      *) warning "File $Passwordfile
  should be readable by current user only.
  Please set access permissions to 600 or 400." ;;
    esac
  }
  [ -z "$Containeruserpassword" ] && Containeruserpassword='sac19FwGGTx/A' # password: x11docker

  storeinfo containeruser="$Containeruser"
  store_runoption env "USER=$Containeruser"
  debugnote "container user: $Containeruser $Containeruseruid:$Containerusergid $Containeruserhosthome"
  Containerusergroups="$Containerusergroups $Containerusergroup"

  case "$Backend" in
    docker|podman|nerdctl|proot)
      case "$Containersetup" in
        no)  store_runoption env "XDG_RUNTIME_DIR=/tmp" ;;
      esac
    ;;
    host) ;;
  esac
  return 0
}
create_backendcommand() {       ### create command to run docker|podman|nerdctl ###
  local Line= Memory Tini= Grouplist=

  case "$Backend" in
    docker|podman|nerdctl)
      Backendcommand="$Backendbin run"
      $Backendbin run --help 2>/dev/null | grep -q -- '--pull' && Backendcommand="$Backendcommand \\
  --pull never"
    ;;
    proot)
      Backendcommand="proot \\
  --rootfs='$Rootfs' \\
  --bind=/etc/host.conf \\
  --bind=/etc/nsswitch.conf \\
  --bind=/etc/resolv.conf \\
  --bind=/proc \\
  --bind=/sys \\
  --bind=/dev \\
  --bind=/run/shm"
    ;;
    chroot)
      Backendcommand="chroot"
      Chrootmountlist="
/sys|$Rootfs/sys
/proc|$Rootfs/proc
/dev|$Rootfs/dev
/dev/shm|$Rootfs/dev/shm
/run/shm|$Rootfs/run/shm
/dev/pts|$Rootfs/dev/pts"
      Chrootmountlist=""
    ;;
  esac

  # --keepcache
  case "$Backend" in
    docker|podman)
      [ "$Preservecachefiles" = "no" ] && Backendcommand="$Backendcommand \\
  --rm"
    ;;
  esac

  # --sudouser
  [ "$Sudouser" = "yes" ] && {
    case "$Backend" in
      proot)
        Backendcommand="$Backendcommand \\
  --root-id"
      ;;
    esac
  }

  # --interactive
  case "$Interactive" in
    yes)
      case "$Backend" in
        docker|podman|nerdctl)
          Backendcommand="$Backendcommand \\
  --interactive \\
  --tty"
        ;;
      esac
    ;;
    no)
      case "$Backend" in
        docker|podman|nerdctl)
#          Backendcommand="$Backendcommand \\
#  --detach"
        ;;
      esac
      case "$Backend" in
        docker|podman) Backendcommand="$Backendcommand \\
  --tty" ;;
      esac
    ;;
  esac

  # --name
  [ -z "$Containername" ] && Containername="x11docker_X${Newdisplaynumber}_${Codename}_${Cachenumber}"
  case "$Backend" in
    docker|podman|nerdctl)
      Backendcommand="$Backendcommand \\
  --name $Containername"
      storeinfo "containername=$Containername"
    ;;
  esac

  # --limit
  [ "$Limitresources" ] && {
    case "$Backend" in
      docker|podman|nerdctl)
        Memory="$(awk "BEGIN {print int($(LC_ALL=C free -b | grep "Mem:" | awk ' {print $4 + $6}') * $Limitresources)}")"
        Backendcommand="$Backendcommand \\
  --cpus=$(awk "BEGIN {print $(nproc) * $Limitresources}") \\
  --memory=$Memory \\
  --kernel-memory=$Memory"
      ;;
    esac
  }

  # --user
  case "$Initsystem" in
    none|tini|dockerinit)
      case "$Switchcontaineruser" in
        no)
          case "$Backend" in
            docker|podman|nerdctl)
              [ "$Createcontaineruser" = "yes" ] && Backendcommand="$Backendcommand \\
  --user $Containeruseruid:$Containerusergid"
            ;;
            chroot)
              [ "$Createcontaineruser" = "yes" ] && Backendcommand="$Backendcommand \\
  --userspec=$Containeruseruid:$Containerusergid"
            ;;
          esac
        ;;
        yes)
          case "$Backend" in
            docker|podman|nerdctl)
              Backendcommand="$Backendcommand \\
  --user 0:0"
            ;;
            chroot)
              Backendcommand="$Backendcommand \\
  --userspec=0:0"
            ;;
          esac
        ;;
      esac
    ;;
    systemd|runit|openrc|sysvinit|s6-overlay)
      # init systems switch later from root to user.
      case "$Backend" in
        docker|podman|nerdctl)
          Backendcommand="$Backendcommand \\
  --user root"
        ;;
        chroot)
          Backendcommand="$Backendcommand \\
  --userspec=0:0"
        ;;
      esac
    ;;
  esac

  # userns
  [ "$Createcontaineruser" = "yes" ] && {
    # Disable user namespacing to avoid file permission issues with --home or --share. Files need same UID/GID.
    case "$Backend" in
      podman)
        Backendcommand="$Backendcommand \\
  --userns=keep-id"
      ;;
      docker)
        case "$Runtime" in
          sysbox-runc)
          ;;
          *)
            [ "$Rootlessbackend" = "yes" ] || Backendcommand="$Backendcommand \\
  --userns=host"  ### FIXME option deprecated?
          ;;
        esac
      ;;
    esac
  }

  # --group-add
  [ "$Switchcontaineruser" = "no" ] && {
    case "$Backend" in
      docker|podman|nerdctl)
        for Line in $Containerusergroups; do
          getent group "${Line:-nonsense}" >/dev/null && Backendcommand="$Backendcommand \\
  --group-add $(getent group "$Line" | cut -d: -f3)"
        done
      ;;
      chroot)
        for Line in $Containerusergroups; do
          getent group "${Line:-nonsense}" >/dev/null && Grouplist="$Grouplist,$(getent group "$Line" | cut -d: -f3)"
        done
        Grouplist="${Grouplist#,}"
        Backendcommand="$Backendcommand \\
  --groups=$Grouplist"
      ;;
    esac
  }

  # --runtime
  [ "$Runtime" ] && {
    case "$Backend" in
      docker|podman|nerdctl)
        Backendcommand="$Backendcommand \\
  --runtime='$Runtime'"
      ;;
    esac
  }

  # --ipc
  [ "$Shareipc" != "no" ] && {
    case "$Backend" in
      docker|podman|nerdctl)
        Backendcommand="$Backendcommand \\
  --ipc $Shareipc"
      ;;
    esac
  }

  # --network
  [ -n "$Network" ] && {
    case "$Backend" in
      docker|podman|nerdctl)
        Backendcommand="$Backendcommand \\
  --network $Network"
      ;;
    esac
  }

  # capabilities
  case "$Backend" in
    docker|podman|nerdctl)
      [ "$Capdropall" = "yes" ] && Backendcommand="$Backendcommand \\
  --cap-drop ALL"
      while read Line ; do
        Backendcommand="$Backendcommand \\
  --cap-add $Line"
      done < <(store_runoption dump cap)
    ;;
  esac

  # --newprivileges
  case "$Backend" in
    docker|podman|nerdctl)
      [ "$Allownewprivileges" = "no" ] && Backendcommand="$Backendcommand \\
  --security-opt no-new-privileges"
    ;;
  esac

  # SELinux restrictions for containers must be disabled to allow access to X socket. Flags z or Z do not help. ### FIXME check for possible change meanwhile
  case "$Backend" in
    docker|podman|nerdctl)
      Backendcommand="$Backendcommand \\
  --security-opt label=type:container_runtime_t"
    ;;
  esac

  # --init
  case "$Initsystem" in
    dockerinit)
      case "$Backend" in
        docker|podman|nerdctl)
          Backendcommand="$Backendcommand \\
  --init"
        ;;
      esac
    ;;
    tini)
      Tini="$Tinicontainerpath --"
      case "$Backend" in
        docker|podman|nerdctl)
          Backendcommand="$Backendcommand \\
  --mount $(convertpath mount "$Tinibinaryfile:ro" "$Tinicontainerpath")"
        ;;
        proot)
          Backendcommand="$Backendcommand \\
  --bind=$Tinibinaryfile:$Tinicontainerpath"
        ;;
        chroot)
          cp "$Tinibinaryfile" "$Rootfs/$Tinicontainerpath"
        ;;
      esac
    ;;
    systemd)
      Backendcommand="$Backendcommand \\
  --tmpfs /var/lib/journal"
  esac
  case "$Initsystem" in
    systemd|sysvinit|openrc|runit)
      case "$Backend" in
        podman)
          Backendcommand="$Backendcommand \\
  --systemd=always"
        ;;
      esac
      [ "$Remountcgroup" = "yes" ] && {
        $Backendbin run --help | grep -q -- '--cgroupns' && Backendcommand="$Backendcommand \\
  --cgroupns private"
      }
    ;;
  esac
  
  # stop signal for some init systems
  [ "$Stopsignal" ] && {
    case "$Backend" in
      docker|podman|nerdctl)
        Backendcommand="$Backendcommand \\
  --stop-signal $Stopsignal"
      ;;
    esac
  }

  # --sharecgroup
  [ "$Sharecgroup" = "yes" ] && {
    case "$Backend" in
      docker|podman|nerdctl)
        Backendcommand="$Backendcommand \\
  --mount type=bind,source=/sys/fs/cgroup,target=/sys/fs/cgroup,readonly"
      ;;
    esac
  }

  # Needed especially for --init=systemd and --dbus=system
  case "$Backend" in
    docker|podman)
      Backendcommand="$Backendcommand \\
  --tmpfs /run:exec \\
  --tmpfs /run/lock \\
  --tmpfs /tmp"
    ;;
  esac

  # shared x11docker cache folder
  case "$Backend" in
    docker|podman|nerdctl)
      Backendcommand="$Backendcommand \\
  --mount $(convertpath mount "$Sharefolder:rw" $Sharefoldercontainer)"
    ;;
    proot)
      Backendcommand="$Backendcommand \\
  --bind=$Sharefolder:$Sharefoldercontainer"
    ;;
    chroot)
      Chrootmountlist="$Chrootmountlist
$Sharefolder|$Rootfs/$Sharefoldercontainer"
    ;;
  esac

  # --home
  case "$Sharehome" in
    host)
      case "$Backend" in
        docker|podman|nerdctl)
          Backendcommand="$Backendcommand \\
  --mount $(convertpath mount "$Persistanthomevolume:rw" "$Containeruserhome")"
        ;;
        proot)
          Backendcommand="$Backendcommand \\
  --bind=$Persistanthomevolume:$Containeruserhome"
        ;;
        chroot)
          Chrootmountlist="$Chrootmountlist
$Persistanthomevolume|$Rootfs/$Containeruserhome"
        ;;
      esac
    ;;
    volume)
      case "$Backend" in
        docker|podman|nerdctl)
          Backendcommand="$Backendcommand \\
  --volume $(convertpath volume "$Persistanthomevolume" "$Containeruserhomebasefolder")" ### FIXME $Containeruserhomebasefolder ?? mount/volume?
        ;;
        *)
          error "--backend=$Backend: Sharing a docker volume is not supported.
  Failing option: --home=$Persistanthomevolume"
        ;;
      esac
    ;;
  esac

  # --share
  while read -r Line; do
    case "$Backend" in
      docker|podman|nerdctl)
        case "$(cut -c1-5 <<< "$Line")" in
          "/dev/")
             Backendcommand="$Backendcommand \\
  --device $(convertpath volume "$Line")"
            warning "Sharing device file: $Line"
          ;;
          *)
            Backendcommand="$Backendcommand \\
  --mount $(convertpath mount "$Line")"
          ;;
        esac
      ;;
      proot)
        grep -q ':ro' <<< "$Line" && {
          warning "--backend=proot cannot mount read-only:
  $Line
  Fallback: mounting with r/w"
          check_fallback
        }
        Line="${Line//":ro"/""}"
        Backendcommand="$Backendcommand \\
  --bind=$Line:$Line"
      ;;
      chroot)
        Chrootmountlist="$Chrootmountlist
$Line|$Rootfs/$Line"
      ;;
    esac
  done < <(store_runoption dump volume)

  # --gpu: share NVIDIA driver installer
  [ -f "$Nvidiainstallerfile" ] && {
    case "$Backend" in
      docker|podman|nerdctl)
        Backendcommand="$Backendcommand \\
  --mount $(convertpath mount "$Nvidiainstallerfile:ro" "$Nvidiacontainerfile")"
      ;;
      proot)
        Backendcommand="$Backendcommand \\
  --bind=$Nvidiainstallerfile:$Nvidiacontainerfile"
      ;;
      chroot)
        Chrootmountlist="$Chrootmountlist
$Nvidiainstallerfile|$Rootfs/$Nvidiacontainerfile"
      ;;
    esac
  }

  # --xc
  case "$Xcontainer" in
    yes)
      [ "$Newxsocket" ] && {
        case "$Backend" in
          docker|podman|nerdctl)
            Backendcommand="$Backendcommand \\
  --mount $(convertpath mount "$Cachefolder/tmp/.X11-unix/X$Newdisplaynumber:ro" "$Newxsocket")"
            [ "$Shareipc" = "no" ] && [ "$Containeruser" = "$Hostuser" ] && [ "$Xcontainerbackend" = "$Backend" ] && {
              case "$Runtime" in
                kata-runtime|sysbox-runc) ;;
                *)
                  Backendcommand="$Backendcommand \\
  --ipc=container:$Xcontainername"
                ;;
              esac
            }
          ;;
          proot)
            Backendcommand="$Backendcommand \\
  --bind=$Cachefolder/tmp/.X11-unix/X$Newdisplaynumber:$Newxsocket"
          ;;
        esac
      }
      [ "$Virgl" = "yes" ] && Backendcommand="$Backendcommand \\
  --mount $(convertpath mount "$Cachefolder/tmp/.virgl_test" "/tmp/.virgl_test")"
    ;;
    no)
      [ "$Newxsocket" ] && {
        case "$Backend" in
          docker|podman|nerdctl)
            Backendcommand="$Backendcommand \\
  --mount $(convertpath mount "$Newxsocket:ro")"
          ;;
          proot)
            Backendcommand="$Backendcommand \\
  --bind=$Newxsocket:$Newxsocket"
          ;;
          chroot)
            Chrootmountlist="$Chrootmountlist
$Newxsocket|$Rootfs/$Newxsocket"
          ;;
        esac
      }
    ;;
  esac

  # Wayland socket will be softlinked to XDG_RUNTIME_DIR in containerrc
  [ "$Setupwayland" = "yes" ] && {
    case "$Containersetup" in
      yes)
        case "$Xcontainer" in
          yes)
            case "$Backend" in
              docker|podman|nerdctl)
                Backendcommand="$Backendcommand \\
  --mount $(convertpath mount "$Cachefolder/XDG_RUNTIME_DIR/$Newwaylandsocket" "/$Newwaylandsocket")"
              ;;
              proot)
                Backendcommand="$Backendcommand \\
  --bind=$Cachefolder/XDG_RUNTIME_DIR/$Newwaylandsocket:/$Newwaylandsocket"
              ;;
            esac
          ;;
          no)
            case "$Backend" in
              docker|podman|nerdctl)
                Backendcommand="$Backendcommand \\
  --mount $(convertpath mount "$XDG_RUNTIME_DIR/$Newwaylandsocket" "/$Newwaylandsocket")"
              ;;
              proot)
                Backendcommand="$Backendcommand \\
  --bind=$XDG_RUNTIME_DIR/$Newwaylandsocket:/$Newwaylandsocket"
              ;;
            esac
          ;;
        esac
      ;;
      no)
        case "$Xcontainer" in
          yes)
            case "$Backend" in
              docker|podman|nerdctl)
                Backendcommand="$Backendcommand \\
  --mount $(convertpath mount "$Cachefolder/XDG_RUNTIME_DIR/$Newwaylandsocket")"
              ;;
              proot)
                Backendcommand="$Backendcommand \\
  --bind=$Cachefolder/XDG_RUNTIME_DIR/$Newwaylandsocket" ### FIXME looks wrong
              ;;
            esac
          ;;
          no)
            case "$Backend" in
              docker|podman|nerdctl)
                Backendcommand="$Backendcommand \\
  --mount $(convertpath mount "$XDG_RUNTIME_DIR/$Newwaylandsocket")"
              ;;
              proot)
                Backendcommand="$Backendcommand \\
  --bind=$XDG_RUNTIME_DIR/$Newwaylandsocket"
              ;;
            esac
          ;;
        esac
      ;;
    esac
  }

  # --pulseaudio
  case "$Pulseaudiomode" in
    socket)
      case "$Backend" in
        docker|podman|nerdctl)
          Backendcommand="$Backendcommand \\
  --mount $(convertpath mount "$Pulseaudioconf:ro" "/etc/pulse/client.conf")"
          ;;
        proot)
          Backendcommand="$Backendcommand \\
  --bind=$Pulseaudioconf:/etc/pulse/client.conf"
        ;;
      esac
    ;;
    host)
      case "$Backend" in
        docker|podman|nerdctl)
          Backendcommand="$Backendcommand \\
  --mount $(convertpath mount "$Pulseaudiosocket:ro" "/tmp/pulseaudio.socket.host") \\
  --mount $(convertpath mount "$Pulseaudioconf:ro" "/etc/pulse/client.conf")"
          ;;
        proot)
          Backendcommand="$Backendcommand \\
  --bind=$Pulseaudiosocket:/tmp/pulseaudio.socket.host \\
  --bind=$Pulseaudioconf:/etc/pulse/client.conf"
        ;;
      esac
    ;;
  esac

  # --workdir
  case "$Containersetup" in
    yes)
      case "$Backend" in
        docker|podman|nerdctl)
          Backendcommand="$Backendcommand \\
  --workdir '${Workdir:-/tmp}'"
        ;;
        proot)
          Backendcommand="$Backendcommand \\
  --cwd=${Workdir:-/tmp}"
        ;;
      esac
    ;;
    no)
      [ "$Workdir" ] && {
        case "$Backend" in
          docker|podman|nerdctl)
            Backendcommand="$Backendcommand \\
  --workdir '$Workdir'"
          ;;
          proot)
            Backendcommand="$Backendcommand \\
  --cwd=${Workdir}"
          ;;
        esac
      }
    ;;
  esac

  # --no-setup, --no-entrypoint
  case "$Containersetup" in
    yes)
      case "$Backend" in
        docker|podman|nerdctl)
          Backendcommand="$Backendcommand \\
  --entrypoint env"
        ;;
      esac
    ;;
  esac

  # --env: add environment variables. Only needed here for possible 'docker exec'. Also set in containerrc
  while read Line; do
    case "$Backend" in
      docker|podman|nerdctl)
        Backendcommand="$Backendcommand \\
  --env '$(escapestring "$Line")'"
      ;;
    esac
  done < <(store_runoption dump env)

  # add custom docker arguments, imagename and imagecommand
  [ "$Customdockeroptions" ] && {
    case "$Backend" in
      docker|podman|nerdctl|proot)
        Backendcommand="$Backendcommand \\
  $Customdockeroptions"
      ;;
    esac
  }

  case "$Backend" in
    docker|podman|nerdctl)
      Backendcommand="$Backendcommand \\
  --"
    ;;
    proot)
      Backendcommand="$Backendcommand \\
  "
    ;;
    chroot)
      Backendcommand="$Backendcommand \\
  $Rootfs \\
  "
    ;;
  esac

  case "$Containersetup" in
    yes)
      case "$Switchcontaineruser" in
        no)
          case "$Backend" in
            docker|podman|nerdctl)
              Backendcommand="$Backendcommand $Imagename $Tini /bin/sh - $(convertpath share "$Containerrc")"      # start_container runs containerrootrc with 'docker exec'
            ;;
            proot)
              Backendcommand="$Backendcommand /bin/sh - $(convertpath share "$Containerrc")"
            ;;
            chroot)
              Backendcommand="$Backendcommand /bin/sh - $(convertpath share "$Containerrootrc")"
            ;;
          esac
        ;;
        yes)
          case "$Backend" in
            docker|podman|nerdctl)
              Backendcommand="$Backendcommand $Imagename $Tini /bin/sh - $(convertpath share "$Containerrootrc")"  # containerrootrc runs containerrc
            ;;
            proot)
              Backendcommand="$Backendcommand /bin/sh - $(convertpath share "$Containerrootrc")"
            ;;
            chroot)
              Backendcommand="$Backendcommand /bin/sh - $(convertpath share "$Containerrootrc")"
            ;;
          esac
        ;;
      esac
    ;;
    no)
      case "$Backend" in
        docker|podman|nerdctl)
          Backendcommand="$Backendcommand $Imagename $Containercommand"
        ;;
        proot)
          Backendcommand="$Backendcommand $Containercommand"
        ;;
        chroot)
          Backendcommand="$Backendcommand $Containercommand"
        ;;
      esac
    ;;
  esac

  return 0
}
setup_capabilities() {          # check linux capabilities needed by container
  # compare: man capabilities

  [ "$Sudouser" ]               && Adminusercaps="yes"
  [ "$Capdropall" = "no" ]      && [ "$Allownewprivileges" = "auto" ] && {
    note "Option --cap-default: Enabling option --newprivileges=yes.
  You can avoid this with --newprivileges=no"
    Allownewprivileges="yes"
  }

  # --sudouser
  [ "$Sudouser" ] && warning "Option --sudouser severely reduces container security.
  Container gains additional capabilities to allow sudo and su.
  If an application breaks out of container, it can harm your system
  in many ways without you noticing. Default password: x11docker"

  # enable dbus
  [ "$Dbussystem" = "yes" ]    && {
    Dbusrunsession="yes"
    store_runoption cap "CHOWN FOWNER" ### FIXME: CHOWN needed indeed here?
    Switchcontaineruser="yes"
  }
  case "$Initsystem" in
    systemd) Dbusrunsession="no" ;;
  esac

  case "$Initsystem" in
    none|tini|dockerinit) ;;
    systemd)
      Switchcontaineruser="yes"
      store_runoption cap "FSETID FOWNER SETPCAP SYS_BOOT"
    ;;
    runit|openrc|sysvinit)
      Switchcontaineruser="yes"
      store_runoption cap "SYS_BOOT KILL"
    ;;
    s6-overlay)
      Switchcontaineruser="yes"
      store_runoption cap "CHOWN KILL"
    ;;
  esac

  [ "$Sharecgroup" = "yes" ]         && Switchcontaineruser="yes" # needed for elogind
  [ "$Switchcontaineruser" = "yes" ] && Switchcontainerusercaps="yes"

  [ "$Adminusercaps" = "yes" ] && {
    Switchcontainerusercaps="yes"
    store_runoption cap "CHOWN KILL FSETID FOWNER SETPCAP"
    [ "$Allownewprivileges" = "auto" ] && {
      note "Option --sudouser: Enabling option --newprivileges=yes.
  You can avoid this with --newprivileges=no"
      Allownewprivileges="yes"
    }
  }
  [ "$Switchcontainerusercaps" = "yes" ] && store_runoption cap "SETUID SETGID DAC_OVERRIDE AUDIT_WRITE"

  # Automated NVIDIA driver installation
  [ "$Sharegpu" = "yes" ] && [ "$Nvidiainstallerfile" ] && [ "$Switchcontaineruser" = "yes" ] && store_runoption cap "CHOWN FOWNER"

  [ "$Allownewprivileges" = "auto" ] && Allownewprivileges="no"

  [ "$Allownewprivileges" = "yes" ] && warning "Option --newprivileges=yes: x11docker does not set
  docker run option --security-opt=no-new-privileges.
  That degrades container security.
  However, this is still within a default docker setup."

  # Issues with hidepid=2 seen on NixOS (issue #83)
  { [ "$Switchcontaineruser" = "yes" ] || [ "$Containeruser" != "$Hostuser" ] ; } && {
    [ "$Hostcanwatchroot" = "no" ] && {
      [ "$Hosthidepid" = "yes" ]            && Message="/proc is mounted with hidepid=2." || Message="Cannot watch processes of other users for unknown reasons."
      Message="$Message
  x11docker cannot watch processes of root
  or other users different from $Hostuser."
      [ "$Hostuser" != "$Containeruser" ]   && Message="$Message
  Container user $Containeruser is different from host user $Hostuser."
      [ "$Switchcontaineruser" = "yes" ]    && Message="$Message
  Container PID 1 will run as root."
      Message="$Message
  Therefore x11docker cannot watch container processes
  for a clean termination of X and x11docker itself.
  Four possible solutions:
    1. Run x11docker as root.
    2. Don't use options like --user or --init=systemd that change container user.
    3. Add user $Hostuser to group 'proc'.
    4. Change /proc mount option hidepid=2 to hidepid=1."
      error "$Message"
    }
  }

  return 0
}
setup_initsystem() {            # option init: set up capabilities, check or create files
  # some init system setup also in containerrootrc
  local Message=

  # --init in Mobyvm. /usr/bin/docker-init is not available in MSYS2/Cygwin/WSL1
  case "$Mobyvm" in
    yes) [ "$Initsystem" = "tini" ] && Initsystem="dockerinit" ;;
  esac

  case "$Backend" in
    proot|chroot|host)
      case "$Initsystem" in
        tini|none) ;;
        *)
          note "Option --init: Only --init[=tini] or --init=none are
  supported with option --backend=$Backend. Fallback: Setting option --init=tini"
          check_fallback
          Initsystem="tini"
        ;;
      esac
    ;;
  esac

  case "$Backend" in
    docker|podman|nerdctl)
      store_runoption env "container=$Backend"   # At least OpenRC and systemd regard this hint
    ;;
  esac

  case "$Initsystem" in
    none|dockerinit) ;;
    tini)
      Tinibinaryfile="$(command -v docker-init ||:)"
      [ -z "$Tinibinaryfile" ]                                      && Tinibinaryfile="/snap/docker/current/bin/docker-init"
      [ -e "$Tinibinaryfile" ]                                      || Tinibinaryfile="/snap/docker/current/usr/bin/docker-init"
      [ -e "/usr/bin/tini-static" ]                                 && Tinibinaryfile="/usr/bin/tini-static"
      [ -e "/usr/local/share/x11docker/tini-static" ]               && Tinibinaryfile="/usr/local/share/x11docker/tini-static"
      [ -e "$Localsharedir/tini-static" ]                           && Tinibinaryfile="$Localsharedir/tini-static"
      Tinibinaryfile="$(myrealpath "$Tinibinaryfile" 2>/dev/null ||:)"
      [ -e "$Tinibinaryfile" ]                                      || Tinibinaryfile=""
      [ "$Tinibinaryfile" ] && {
        case "$Runtime" in
          kata-runtime)
            # avoid sharing same file that might be shared with runc already.
            cp -u "$Tinibinaryfile"  "$Localsharedir/tini-static-kata"
            Tinibinaryfile="$Localsharedir/tini-static-kata"
          ;;
        esac
        [ -x "$Tinibinaryfile" ] || {
          chmod +x "$Tinibinaryfile" || {
            warning "Your tini binary is not executable. Please run
    chmod +x $Tinibinaryfile"
            Initsystem="none"
          }
        }
      } || {
        Initsystem="none"
      }
      [ "$Initsystem" = "none" ] && {  ### FIXME --backend=host,proot
        note "Did not find container init system 'tini'.
  This is a bug in your distributions docker package.
  Normally, docker provides init system tini as '/usr/bin/docker-init'.

  x11docker uses tini for clean process handling and fast container shutdown.
  To provide tini yourself, please download tini-static:
    https://github.com/krallin/tini/releases/download/v0.18.0/tini-static
  Store it in one of:
    $Localsharedir/
    /usr/local/share/x11docker/"
      }
      verbose "--init: Found tini binary: ${Tinibinaryfile:-(none)}"
      [ "$Tinibinaryfile" ] && storeinfo "tini=$Tinibinaryfile"
    ;;
    systemd)
      Stopsignal="SIGRTMIN+3"
      Containerusergroups="$Containerusergroups systemd-journal"
    ;;
    runit)
      Stopsignal="HUP"
      store_runoption env "VIRTUALIZATION=docker"
    ;;
    openrc)
    ;;
    sysvinit)
      Stopsignal="INT"
    ;;
    s6-overlay)
    ;;
  esac

  case "$Initsystem" in
    systemd)
      warning "Option --init=systemd slightly degrades container isolation.
  It adds some user switching capabilities x11docker would drop otherwise.
  However, they are still within default docker capabilities.
  Not within default docker capabilities it adds capability SYS_BOOT.
  Some processes in container will run as root."
    ;;
    runit|openrc|sysvinit)
      warning "Option --init=$Initsystem slightly degrades container isolation.
  It adds some user switching capabilities x11docker would drop otherwise.
  However, they are still within default docker capabilities.
  Not within default docker capabilities it adds capability SYS_BOOT.
  Some processes in container will run as root."
    ;;
    s6-overlay)
      warning "Option --init=$Initsystem slightly degrades container isolation.
  It adds some user switching capabilities x11docker would drop otherwise.
  However, they are still within default docker capabilities.
  Some processes in container will run as root."
    ;;
    tini|none|dockerinit)
      [ "$Dbussystem" = "yes" ] && {
        [ "$Capdropall" = "yes" ] && warning "Option --dbus=system slightly degrades container isolation.
  It adds some user switching capabilities x11docker would drop otherwise.
  However, they are still within default docker capabilities.
  Some processes in container will run as root.
  --dbus=system might need further capabilities or --cap-default to work
  as expected. If in doubt, one of --init=systemd|openrc|runit|sysvinit
  might be a better choice."
        note "Option --dbus=system with init system '$Initsystem'
  can have a quite long timeout delay until startup.
  Use one of --init=systemd|openrc|runit|sysvinit in that case."
      }
    ;;
  esac

  case "$Backend" in
    host)
      [ "$Initsystem" = "tini" ] && {
        Containerentrypoint="$Tinibinaryfile --"
        store_runoption env "TINI_SUBREAPER=1"
      }
    ;;
  esac
  
  case "$Initsystem" in
    systemd|sysvinit|openrc|runit)
      check_cgroup ||:
    ;;
  esac
  
  return 0
}
store_runoption() {             # store env, cap or volume/device for docker command
  # $1  env     store environment variable $2
  #     volume  store volume or device path $2
  #     cap     store capability $2
  #     dump    dump all entries of $2
  local Count Line Path Readwritemode
  case "${1:-}" in
    env)
      Containerenvironmentcount="$((Containerenvironmentcount + 1))"
      Containerenvironment[$Containerenvironmentcount]="${2:-}"
    ;;
    volume)
      Path="$(convertpath subsystem "${2:-}")"
      Readwritemode="$(echo "${2:-}" | rev | cut -c1-3 | rev)"
      [ "$Readwritemode" = ":ro" ] || Readwritemode=""
      Path="$(convertpath subsystem "${2:-}")"
      Readwritemode="$(echo "${2:-}" | rev | cut -c1-3 | rev)"
      [ "$Readwritemode" = ":ro" ] || Readwritemode=""
      case "${Path:0:1}" in
        "/") # path on host
          Sharevolumescount="$((Sharevolumescount + 1))"
          Sharevolumes[$Sharevolumescount]="${2:-}"
          [ -h "$Path" ] && myrealpath "$Path" >/dev/null && {
            note "Option --share: Shared file is a symbolic link. Sharing target, too.
  Symlink: $Path
  Target:  $(myrealpath "$Path")"
            store_runoption volume "$(myrealpath "$Path")$Readwritemode"
          }
        ;;
        *) # Docker volume
          Sharevolumescount="$((Sharevolumescount + 1))"
          Sharevolumes[$Sharevolumescount]="${2:-}"
        ;;
      esac
    ;;
    cap)
      for Line in ${2:-} ; do
        Capabilities="$Capabilities
$Line"
      done
    ;;
    dump)
      case "${2:-}" in
        env) for ((Count=$Containerenvironmentcount ; Count>=1 ; Count --)) ; do echo "${Containerenvironment[$Count]}" ; done ;;
        volume) for ((Count=1 ; Count<=$Sharevolumescount ; Count ++))      ; do echo "${Sharevolumes[$Count]}" ; done ;;
        cap)
          while read Line; do
            [ "$Line" ] && case "$Capdropall" in
              yes) echo "$Line" ;;
              no)  grep -w -q "$Line" <<< "SETPCAP MKNOD AUDIT_WRITE CHOWN NET_RAW DAC_OVERRIDE FOWNER FSETID KILL SETGID SETUID NET_BIND_SERVICE SYS_CHROOT SETFCAP" || echo "$Line" ;;
            esac
          done < <(echo "$Capabilities" | sort -u)
        ;;
      esac
    ;;
  esac
  return 0
}

#### scripts running in container
create_containerrootrc() {      # This script runs as root in container
  echo "#! /bin/sh

# containerrootrc
# This Script is executed as root in container.
# - Create container user
# - Set time zone
# - Create locale
# - Install NVIDIA driver if requested
# - Set up init system services and DBus for --init=systemd|openrc|runit|sysvinit

# redirect output to have it available before 'docker logs' starts. --init=runit (void) would eat up the output at all for unknown reasons.
exec 5>&1 6>&2
exec 1>>$(convertpath share "$Containerlogfile") 2>&1
"

  declare -f storeinfo
  declare -f rocknroll
  echo "$Messagefifofuncs"

  echo "
Messagefile=$(convertpath share "$Messagefifo")
Storeinfofile='$(convertpath share "$Storeinfofile")'
Timetosaygoodbyefile=$(convertpath share "$Timetosaygoodbyefile")

Containeruser=\"\$(storeinfo dump containeruser)\"
Containeruserhome='$Containeruserhome'

debugnote 'Running containerrootrc: Setup as root in container'

Error=''
for Line in cat chmod chown cut cd cp date echo env export grep id ln ls mkdir mv printf rm sed sh sleep tail touch; do
  command -v \$Line || {
    warning \"ERROR: Command not found in image: \$Line\"
    Error=1
  }
done
[ \"\$Error\" ] && error 'Commands for container setup missing in image.
  You can try with option --no-setup to avoid this error.'

# /etc/profile.d
"
  case "$Initsystem" in
    systemd|openrc|sysvinit|runit)
      echo "
install -m 666 /dev/null /etc/profile.d/90-x11docker-containerrc.sh
echo '
echo > /etc/profile.d/90-x11docker-containerrc.sh
exec /bin/sh $(convertpath share "$Containerrc")
' >> /etc/profile.d/90-x11docker-containerrc.sh
"
    ;;
  esac
  while read Line; do
    echo "echo 'export $Line' >> /etc/profile.d/10-x11docker-env.sh"
  done <<< $(store_runoption dump env)
  echo "
# Container system
Containersystem=\"\$(grep '^ID=' /etc/os-release 2>/dev/null | cut -d= -f2 || echo 'unknown')\"
verbose \"Container system ID: \$Containersystem\"

# Check type of libc
ldd --version 2>&1 | grep -q 'musl libc' && Containerlibc='musl'
ldd --version 2>&1 | grep -q -E 'GLIBC|GNU libc'  && Containerlibc='glibc'
debugnote \"containerrootrc: Container libc: \$Containerlibc\"

# Create some system dirs with needed permissions
mkdir -v -p /var/lib/dbus /var/run/dbus
mkdir -v -p -m 1777 /tmp/.ICE-unix /tmp/.X11-unix /tmp/.font-unix
chmod -c 1777 /tmp/.ICE-unix /tmp/.X11-unix /tmp/.font-unix
"

  [ "$Screensize" ]          && rootrc_xrandr
  [ "$Hostlocaltimefile" ]   && rootrc_timezone
  [ "$Langwunsch" ]          && rootrc_create_locale
  [ "$Nvidiainstallerfile" ] && rootrc_nvidia_installer

  echo "
rocknroll || exit 64
"

  [ "$Createcontaineruser" = "yes" ] && rootrc_setup_user
  echo "
debugnote \"containerrootrc: Container user: \$(id \$Containeruser)
\$(cat /etc/passwd | grep '^\$Containeruser:')\"

# Create HOME
Containeruserhome=\"\$(cat /etc/passwd | grep \"\$Containeruser:\" | cut -d: -f6)\"
Containeruserhome=\"\${Containeruserhome:-/tmp/\$Containeruser}\"
[ -e \"\$Containeruserhome\" ] || {
  mkdir -v -p -m 777 \"\$Containeruserhome\"
  chown -v \"\$Containeruser\":\"\$Containerusergroup\" \"\$Containeruserhome\" && chmod -v 755 \"\$Containeruserhome\"  # can fail depending on capabilities
}
ls -la \"\$Containeruserhome\"
export HOME=\"\$Containeruserhome\"
"

  [ "$Switchcontaineruser" = "yes" ] && rootrc_create_helperscripts
  [ "$Dbussystem" = "yes" ]          && rootrc_prepare_dbus
  case "$Initsystem" in
    openrc)   rootrc_prepare_init_openrc ;;
    runit)    rootrc_prepare_init_runit ;;
    systemd)  rootrc_prepare_init_systemd ;;
    sysvinit) rootrc_prepare_init_sysvinit ;;
  esac
  
  echo "
# disable getty in inittab
[ -e /etc/inittab ] && sed -i 's/.*getty/##getty disabled by x11docker## \0/' /etc/inittab
"

  [ "$Dbussystem" = "yes" ] && {
    echo "
command -v dbus-daemon || note 'DBus not found.
  Cannot run DBus system daemon. Please install dbus in image.'
"
    case "$Initsystem" in
      tini|none|dockerinit) 
        echo "
dbus-daemon --system --fork
"
      ;;
    esac
  }

  [ "$Remountcgroup" = "yes" ] && echo "
# wait for nsenter to remount /sys/fs/cgroup to :rw
for i in 1 2 3 4 5 6 7 8 9 10; do
  test -e /nsenter_is_ready && break
  sleep 1
  debugnote 'containerrootrc: Waiting for nsenter/mount to be ready'
done
rm /nsenter_is_ready
"

  [ "$Runasroot" ] && {
    echo "
# Custom setup root command added with option --runasroot
$Runasroot
"
  }

  echo "
  set -x
rocknroll || exit 64
storeinfo containerrootrc=ready   # signal for containerrc
#exec 1>&5 2>&6
"

  [ "$Interactive" = "yes" ] && echo "
exec 1>&5 2>&6
"

  [ "$Switchcontaineruser" = "yes" ] && {   # if "no", containerrc is executed in command line $Backendcommand, and containerrootrc is started with 'docker exec'
    echo "debugnote 'containerrootrc(): --init=$Initsystem'"
    case "$Initsystem" in
      none|dockerinit)
        case "$Backend" in
          proot)                 echo "exec /bin/sh $(convertpath share "$Containerrc")" ;;
          chroot      )          echo "exec /bin/sh /usr/local/bin/x11docker-su" ;;
          docker|podman|nerdctl) echo "exec /usr/local/bin/x11docker-agetty" ;;
        esac
      ;;
      tini)
        case "$Backend" in
          proot)                 echo "exec env TINI_SUBREAPER=1 '$Tinicontainerpath' /bin/sh $(convertpath share "$Containerrc")" ;;
          chroot)                echo "exec env TINI_SUBREAPER=1 '$Tinicontainerpath' /bin/sh /usr/local/bin/x11docker-su" ;;
          docker|podman|nerdctl) echo "exec '$Tinicontainerpath' -- /usr/local/bin/x11docker-agetty" ;;
        esac
      ;;
      sysvinit)
        echo "/usr/local/bin/x11docker-watch &"
        echo "exec /sbin/init"
      ;;
      openrc)
        echo "/usr/local/bin/x11docker-watch &"
        echo "command -v openrc-init && exec openrc-init || exec /sbin/init"
      ;;
      runit)
        echo "/usr/local/bin/x11docker-watch &"
        echo "[ -e /sbin/runit-init ] && exec runit-init || exec /sbin/init"
      ;;
      s6-overlay)
        echo "exec /init /usr/local/bin/x11docker-agetty"
      ;;
      systemd)
        echo 'Systemd=/lib/systemd/systemd'
        echo '[ -x "$Systemd" ] || Systemd=/bin/systemd'
        echo '[ -x "$Systemd" ] || Systemd=/sbin/systemd'
        echo '[ -x "$Systemd" ] || Systemd=/sbin/init'
        echo 'command -v systemctl >/dev/null || {'
        echo '  error "Option --init=systemd: systemd not found."'
        echo '  exit 64'
        echo '}'
        echo 'export SYSTEMD_LOG_LEVEL=info'
        echo 'export SYSTEMD_LOG_TARGET=console'
        echo 'exec $Systemd --show-status=yes'
      ;;
    esac
  }
  return 0
}
create_containerrc() {          # This script runs as unprivileged user in container
  local Ungrep= Path= Line=

  {
    echo "#! /bin/sh
# containerrc
# Created startscript for docker run used as container command.
# Runs as unprivileged user in container.

[ '$Interactive' = 'no' ] && exec >> $(convertpath share "$Containerlogfile") 2>&1

$(declare -f mysleep)
$(declare -f pspid)
$(declare -f rocknroll)
$(declare -f saygoodbye)
$(declare -f storeinfo)
$(declare -f storepid)
$(declare -f waitforlogentry)
$Messagefifofuncs

Messagefile=$(convertpath share "$Messagefifo")
Storeinfofile=$(convertpath share "$Storeinfofile")
Storepidfile=$Sharefoldercontainer/store.pids
Timetosaygoodbyefile=$(convertpath share "$Timetosaygoodbyefile")

waitforlogentry containerrc \$Storeinfofile containerrootrc=ready '' infinity
debugnote 'Running containerrc: Unprivileged user commands in container'

verbose \"containerrc: Container system:
\$(cat /etc/os-release 2>&1 ||:)\"

"
    case "$Backend" in
      proot|chroot)
        # set another hostname ### FIXME fails
        sed "s/$(hostname)/$Backend-$Imagebasename/g" "/etc/hosts" > "$Rootfs/etc/hosts"
        echo "$Backend-$Imagebasename"                             > "$Rootfs/etc/hostname"
        # clean environment variables
        while read -r Line; do
          Line="$(cut -d= -f1 <<< "$Line")"
          [ -n "$Line" ] && {
            echo "unset $Line"
            echo "export $Line"
          }
        done < <(unset -f rmcr ; env)
        echo "export PATH='/usr/local/bin:/usr/bin:/bin:/usr/local/games:/usr/games:/sbin:/usr/sbin'"
        echo 'export PS1="\u@'"$Backend"'-'"$Imagebasename"':\w\$ "' ### FIXME fails
      ;;
      docker|podman|nerdctl)
        [ "$Switchcontaineruser" = "yes" ] && {
          echo '# Environment variables found in image:'
          IFS=$'\n'
          while read -r Line; do
            echo "export $(escapestring "$Line")"
          done < <($Backendbin run --rm --entrypoint env "$Imagename" env 2>> "$Containerlogfile" | rmcr | grep -v 'HOSTNAME=' ||: )
          IFS=$' \t\n'
        }
      ;;
    esac
    
    [ "$Initsystem" = 'systemd' ] && echo "systemctl --user start dbus"  ### FIXME

    echo "
# USER and HOME
Containeruser='$(storeinfo dump containeruser)'
export USER=\"\$Containeruser\"
"

    case "$Createcontaineruser" in
      yes)
        echo "Containeruserhome='$Containeruserhome'"
      ;;
      no)
        case "$Sharehome" in
          no)
            echo "Containeruserhome=\"\$(cat /etc/passwd | grep \"\$Containeruser:.:\" | cut -d: -f6)\""
            echo "Containeruserhome=\"\${Containeruserhome:-/tmp/\$Containeruser}\""
            echo "mkdir -p \"\$Containeruserhome\""
          ;;
          volume)
            echo "Containeruserhome='$Containeruserhome'"
          ;;
        esac
      ;;
    esac

    echo "
[ \"\$Containeruserhome\" ] && export HOME=\"\$Containeruserhome\"

# XDG_RUNTIME_DIR

Containeruseruid=\$(id -u \$Containeruser)
export XDG_RUNTIME_DIR=/tmp/XDG_RUNTIME_DIR
[ -e /run/user/\$Containeruseruid ] && {
  ln -s /run/user/\$Containeruseruid \$XDG_RUNTIME_DIR
  export XDG_RUNTIME_DIR=/run/user/\$Containeruseruid
} || {
  mkdir -p -m700 \$XDG_RUNTIME_DIR
}
"

    # softlinks from shared folders to HOME
    [ "$Persistanthomevolume" != "$Containeruserhosthome" ] && { # not for --home=$HOME
      while read -r Line; do
        Path="$(convertpath container "$Line")"
        [ "$(cut -c1-5 <<< "$Line")" != "/dev/" ] && {
          [ "$Line" != "$Path" ] && {   # different for paths in HOME without --home
            case "$Line" in
              "$Containeruserhosthome") # --share=$HOME
                echo "ln -s '$Path' -T '$Containeruserhome/home.host.$Containeruser'"
                Ungrep="$Ungrep|home.host.$Containeruser"
              ;;
              *)
                echo "mkdir -p $(dirname "$Line")"
                echo "ln -s '$Path' -T '$(dirname "$Line")'"
                Ungrep="$Ungrep|$(basename "$Line")"
              ;;
            esac
          }
        }
      done < <(store_runoption dump volume)
    }

    echo "
# Copy files from /etc/skel into empty HOME
[ -d \"\$HOME\" ] && {
  [ -d /etc/skel ] && [ -z \"\$(ls -A \"\$Containeruserhome\" 2>/dev/null | grep -v -E \"\.bashrc|\.profile|gnupg${Ungrep}\")\" ] && {
    debugnote \"containerrc: HOME is empty. Copying from /etc/skel\"
    cp -n -R /etc/skel/. \$Containeruserhome
    :
  } || {
    debugnote \"containerrc: HOME is not empty. Not copying from /etc/skel\"
  }
}
"

    [ "$Dbusrunsession" = "yes" ]  && {
      # Check for dbus user daemon command
      echo "command -v dbus-run-session >/dev/null && Dbus=dbus-run-session || note \"Option --dbus: dbus seems to be not installed.
  Cannot run a DBus user session. Please install package dbus in image.\""
      #case "$Initsystem" in
      #  systemd|sysvinit|openrc|runit)
      #    echo "Dbus="
      #  ;;
      #esac
    }
    echo "export DISPLAY='$Newdisplay' XAUTHORITY=$(convertpath share "$Xclientcookie")"
    case "$Xserver" in
      --tty)
        echo "unset DISPLAY WAYLAND_DISPLAY XAUTHORITY" ;;
      --weston|--kwin|--hostwayland)
        echo "unset DISPLAY XAUTHORITY" ;;
      *)
        echo "unset WAYLAND_DISPLAY" ;;
    esac

    [ "$Setupwayland" = "yes" ] && {
      echo "export WAYLAND_DISPLAY='$Newwaylandsocket'"
      echo "ln -s /$Newwaylandsocket \$XDG_RUNTIME_DIR/$Newwaylandsocket"
    } || {
      echo "export XDG_SESSION_TYPE=x11"
    }

    echo "
export TERM=xterm

storeinfo test locale && export LANG=\"\$(storeinfo dump locale)\"

[ -e \"$Hostlocaltimefile\" ]                   || export TZ=$Hostutctime
[ \"\$(date -Ihours)\" != \"$(date -Ihours)\" ] && export TZ=$Hostutctime

[ \"\$DEBIAN_FRONTEND\" = noninteractive ] && unset DEBIAN_FRONTEND && export DEBIAN_FRONTEND
[ \"\$DEBIAN_FRONTEND\" = newt ]           && unset DEBIAN_FRONTEND && export DEBIAN_FRONTEND

# container environment (--env)
"
    case "$Containerusershell" in
      auto) echo "command -v /bin/bash >/dev/null && export SHELL=/bin/bash || export SHELL=/bin/sh" ;;
      *)    echo "export $(escapestring "SHELL=$Containerusershell")" ;;
    esac
    while read -r Line ; do
      echo "export $(escapestring "$Line")"
    done < <(store_runoption dump env)

    [ "$Xauthentication" = 'yes' ] || echo "unset XAUTHORITY && export XAUTHORITY"

    echo "
[ -d \"\$HOME\" ] && cd \"\$HOME\"
[ -n '$Workdir' ] && [ -d '$Workdir' ] && cd '$Workdir'    # WORKDIR in image or option --workdir

unset -f rmcr
env >> $(convertpath share "$Containerenvironmentfile")
verbose \"Container environment:
\$(env | sort)\"
"

    case "$Interactive" in
      no)
        echo "LD_PRELOAD= tail -f $(convertpath share "$Cmdstdoutlogfile")     2>/dev/null & Stdoutpid=\$!"
        echo "LD_PRELOAD= tail -f $(convertpath share "$Cmdstderrlogfile") >&2 2>/dev/null & Stderrpid=\$!"
        case "$Backend" in
          proot|chroot)
            echo 'storepid $Stdoutpid tail_stdout'
            echo 'storepid $Stderrpid tail_stderr'
          ;;
        esac
        echo "exec \$Dbus sh $(convertpath share "$Cmdrc") >> $(convertpath share "$Cmdstdoutlogfile") 2>>$(convertpath share "$Cmdstderrlogfile")"
      ;;
      yes)
        echo "$Cmdexec \$Dbus sh $(convertpath share "$Cmdrc") <&0"
      ;;
    esac
  } >> "$Containerrc"
  return 0
}
create_cmdrc() {                # This script runs as unprivileged user in container and starts the final command
  local Line

  {
    echo "#! /bin/sh
# Created startscript for cmdrc containing final container command

$(declare -f storeinfo)
$(declare -f saygoodbye)
$Messagefifofuncs
Messagefile=$(convertpath share "$Messagefifo")
Storeinfofile=\"$(convertpath share "$Storeinfofile")\"
Timetosaygoodbyefile=$(convertpath share "$Timetosaygoodbyefile")
"

    # --runasuser commands added here
    [ -n "$Runasuser" ] && {
      # Custom daemon commands added with option --runasuser
      while read Line; do
        echo "debugnote 'cmdrc: running --runsasuser command:
  $Line'"
        echo "$Line"
      done <<< "$(grep . <<< "$Runasuser")"
    }

    echo "
debugnote \"cmdrc: Running container command:
  $Containerentrypoint $Containercommand\"
"
    case "$Backend" in
      host|chroot)
        #echo "# close additional file descriptors"
        #echo "for i in 3 4 6 7 8 9; do"
        #echo "  { >&\$i ;} 2>/dev/null && exec >&\$i-"
        #echo "done"
        echo ""
      ;;
    esac

    case "$Forwardstdin" in
      yes) echo "$Containerentrypoint $Containercommand <$(convertpath share "$Cmdstdinfifo")" ;;
      no)  echo "$Containerentrypoint $Containercommand" ;;
    esac
    echo "
export LD_PRELOAD=
storeinfo cmdexitcode=\$?

[ -h \"\$Homesoftlink\" ] && rm \$Homesoftlink
saygoodbye cmdrc
"

  } >> "$Cmdrc"
  return 0
}
# code pieces for containerrootrc
rootrc_create_helperscripts() {
  echo "
# Create some helper scripts

mkdir -p /usr/local/bin

### x11docker-message
echo \"#! /bin/sh
# Send messages to x11docker on host.
# To be sourced by other scripts.
$Messagefifofuncs_escaped
Messagefile=$(convertpath share "$Messagefifo")
\" >/usr/local/bin/x11docker-message

### x11docker-su
echo \"#! /bin/sh
# User switch from root in containerrootrc to unprivileged user in containerrc.
# Called e.g. by x11docker-agetty.
. /usr/local/bin/x11docker-message
debugnote 'Running x11docker-su'
chmod +x $(convertpath share "$Containerrc")
\" >/usr/local/bin/x11docker-su
"
  case "$Backend" in
    proot)                        echo "echo 'exec /bin/sh - $(convertpath share "$Containerrc")'                          >>/usr/local/bin/x11docker-su" ;;
    docker|podman|nerdctl|chroot) echo "echo \"exec su - -s /bin/sh  \$Containeruser $(convertpath share "$Containerrc")\" >>/usr/local/bin/x11docker-su" ;;
  esac

  echo "
chmod +x /usr/local/bin/x11docker-su

### x11docker-agetty
echo \"#! /bin/sh
# Run agetty to get a valid console.
# Needed at least for --interactive.
# Runs x11docker-su or agetty with login
# Called at different places depending on init system.
. /usr/local/bin/x11docker-message
debugnote 'Running x11docker-agetty'
"
  case "$Initsystem" in
    systemd|openrc|sysvinit|runit)
      echo "
[ -e /sbin/agetty ] && exec agetty --autologin \$Containeruser console
" 
    ;;
    *)
      echo "
[ -e /sbin/agetty ] && exec agetty --autologin \$Containeruser -l /usr/local/bin/x11docker-su console
"
    ;;
  esac
  echo "
debugnote 'x11docker-agetty: agetty not found.'
note '/sbin/agetty not found. Startup can fail, --interactive can misbehave.
  Please install package util-linux in image.'
exec /usr/local/bin/x11docker-su
\" >/usr/local/bin/x11docker-agetty
chmod +x /usr/local/bin/x11docker-agetty

### x11docker-watch
echo \"#! /bin/sh
# Wait for end of x11docker and shut down container.
# Started in background by x11docker for sysvinit|runit|openrc.
. /usr/local/bin/x11docker-message
debugnote 'Running x11docker-watch'
read Dummy <$(convertpath share "$Timetosaygoodbyefifo")
echo timetosaygoodbye >>$(convertpath share "$Timetosaygoodbyefifo")
debugnote 'x11docker-watch: $Initsystem shutdown now'
shutdown 0
systemctl poweroff
openrc-shutdown --poweroff 0
halt
halt -f
poweroff
\" >/usr/local/bin/x11docker-watch
chmod +x /usr/local/bin/x11docker-watch

###
"
  return 0
}
rootrc_create_locale() {
  local Line
  while read Line; do
    echo "
# --lang: Language locale $Line
verbose \"Searching for language locale matching $Line\"
Locales=\"\$(locale -a)\"
Langall=\"\$(cat /usr/share/i18n/SUPPORTED | grep -E 'UTF-8|utf8' | cut -d' ' -f1 | cut -d. -f1 | cut -d@ -f1 | sort | uniq)\"
Langland=\"\$(echo $Line | cut -d. -f1)\"
Langcontainer=''

echo \"\$Langland\" | grep -q '_' || {
  Langland=\"\$(echo \$Langland | tr '[:upper:]' '[:lower:]')_\$(echo \$Langland | tr '[:lower:]' '[:upper:]')\"
  echo \"\$Langall\" | grep -q \"\$Langland\" || {
    echo \"\$Langall\" | grep -i -q \"$Line\" && {
      Langland=\"\$(echo \"\$Langall\" | grep -i -m1 \"$Line\")\"
    }
  }
}

Langland=\"\$(echo \"\$Langland\" | cut -d_ -f1 | tr '[:upper:]' '[:lower:]')_\$(echo \"\$Langland\" | cut -d_ -f2 | tr '[:lower:]' '[:upper:]')\"

echo \"\$Locales\" | grep -q \"\$Langland.UTF-8\" && Langcontainer=\"\$Langland.UTF-8\"
echo \"\$Locales\" | grep -q \"\$Langland.utf8\"  && Langcontainer=\"\$Langland.utf8\"

[ -z \"\$Langcontainer\" ] && {
  [ -e /usr/share/i18n/SUPPORTED ] || note \"Option --lang: /usr/share/i18n/SUPPORTED not found.
  Please install package 'locales' in image (belongs to glibc).
  Look here to find a package for your image system:
  https://github.com/mviereck/x11docker/wiki/dependencies#dependencies-in-image\"

  Langcontainer=\"\$Langland.utf8\"
  note \"Option --lang: Generating language locale \$Langcontainer\".
  command -v localedef >/dev/null || note 'Option --lang: Command localedef not found in image.
  Need it for language locale creation.
  Look here to find a package for your image system:
  https://github.com/mviereck/x11docker/wiki/dependencies#dependencies-in-image'
  
  localedef --verbose --force -i \"\$Langland\" -f UTF-8 \$Langcontainer || verbose \"localedef exit code: \$?\"
  
  locale -a | grep -q \"\$Langcontainer\" || {
    note \"Option --lang: Generation of locale \$Langcontainer failed.\"
    Langcontainer=''
  }
} || {
  debugnote \"Option --lang: Found locale in image: \$Langcontainer\"
}

[ \"\$Langcontainer\" ] && {
  storeinfo locale=\"\$Langcontainer\"
  echo \"LANG=\$Langcontainer\" > /etc/default/locale
} || {
  note 'Option --lang: Desired locale for '$Line' not found and not generated.'
}
"
  done < <(tac <<< "$Langwunsch" | grep . ||:)

echo "
debugnote \"Option --lang: Output of locale -a:
\$(locale -a)\"
"
  return 0
}
rootrc_nvidia_installer() {
  # print code to install NVIDIA driver in container
  # $Nvidiainstallerfile must be shared with container as $Nvidiacontainerfile
  ### FIXME $Containerlibc
  echo "
# Install NVIDIA driver
Nvidiaversion=\"\$(nvidia-settings -v 2>/dev/null | grep version | rev | cut -d' ' -f1 | rev)\"
[ \"\$Nvidiaversion\" ] && note \"Found NVIDIA driver \$Nvidiaversion in image.\"
case \"\$Nvidiaversion\" in
  $Nvidiaversion) note 'NVIDIA driver version in image matches version on host. Skipping installation.' ;;
  *)
    Installationwillsucceed=maybe
    case \"\$Containerlibc\" in
      musl) note 'Installing NVIDIA driver in container systems
  based on musl libc like Alpine is not possible due to
  proprietary closed source policy of NVIDIA corporation.'
        Installationwillsucceed=no
      ;;
    esac
    [ \"\$Installationwillsucceed\" = \"maybe\" ] && {
      note 'Installing NVIDIA driver $Nvidiaversion in container.'
      mkdir -m 1777 /tmp2
      # provide fake tools to fool installer dependency check
      ln -s /bin/true /tmp2/modprobe
      ln -s /bin/true /tmp2/depmod
      ln -s /bin/true /tmp2/lsmod
      ln -s /bin/true /tmp2/rmmod
      ln -s /bin/true /tmp2/ld
      ln -s /bin/true /tmp2/objcopy
      ln -s /bin/true /tmp2/insmod
      Nvidiaoptions='--accept-license --no-runlevel-check --no-questions --no-backup --ui=none --no-kernel-module --no-nouveau-check'
      env TMPDIR=/tmp2 PATH=\"/tmp2:\$PATH\" sh $Nvidiacontainerfile -A | grep -q -- '--install-libglvnd'        && Nvidiaoptions=\"\$Nvidiaoptions --install-libglvnd\"
      env TMPDIR=/tmp2 PATH=\"/tmp2:\$PATH\" sh $Nvidiacontainerfile -A | grep -q -- '--no-nvidia-modprobe'      && Nvidiaoptions=\"\$Nvidiaoptions --no-nvidia-modprobe\"
      env TMPDIR=/tmp2 PATH=\"/tmp2:\$PATH\" sh $Nvidiacontainerfile -A | grep -q -- '--no-kernel-module-source' && Nvidiaoptions=\"\$Nvidiaoptions --no-kernel-module-source\"
      env TMPDIR=/tmp2 PATH=\"/tmp2:\$PATH\" sh $Nvidiacontainerfile --tmpdir /tmp2 \$Nvidiaoptions || note 'ERROR: Installation of NVIDIA driver failed.
  Run with option --verbose to see installer output.'
      rm -R /tmp2 && unset TMPDIR
    } || note 'Skipping installation of $Nvidiacontainerfile'
  ;;
esac"
  return 0
}
rootrc_prepare_dbus() {
  echo "
# check /etc/machine-id
[ -s /etc/machine-id ] || dbus-uuidgen >/etc/machine-id
# Prepare DBus services
Unservicelist='
org.bluez
org.bluez.obex
org.freedesktop.hostname1
org.freedesktop.network1
org.freedesktop.resolve1
org.freedesktop.secrets
org.freedesktop.timedate1
org.freedesktop.Tracker1
org.freedesktop.Tracker1.Miner.Extract
org.freedesktop.UDisks2
org.freedesktop.UPower
org.gtk.vfs.UDisks2VolumeMonitor
org.opensuse.CupsPkHelper.Mechanism
com.deepin.daemon.Bluetooth
com.deepin.daemon.Grub2
com.deepin.daemon.Power
com.deepin.lastore
com.deepin.lastore.Smartmirror
com.deepin.sync.Daemon
com.deepin.sync.Helper
com.deepin.userexperience.Daemon
'
#      ### FIXME test
#      Service=/etc/init.d/elogind
#      echo 'output_log=$(convertpath share $Containerlogfile)' >> \$Service
#      echo 'error_log=$(convertpath share $Containerlogfile)' >> \$Service
#      cat \$Service

for Service in /usr/share/dbus-1/system-services/* /usr/share/dbus-1/services/*; do  # find is not available on fedora
  Name=\"\$(cat \$Service | grep Name= | cut -d= -f2)\"
  Command=\"\$(cat \"\$Service\" | grep Exec= | cut -d= -f2)\"
  echo \"\$Unservicelist\" | grep -q -w \"\$Name\" && {
    debugnote \"DBus: Removing \$Name: \$Service\"
    rm \"\$Service\"
  }
  case \"\$Name\" in
"
  [ "$Initsystem" != "systemd" ] && {
    echo "
    org.freedesktop.systemd1|org.freedesktop.hostname1|org.freedesktop.locale1)
      debugnote \"DBus: Removing \$Name: \$Service\"
      rm \"\$Service\"
    ;;
"
  }
  echo "
    org.freedesktop.login1)
      debugnote \"DBus: Found login service \$Name: \$Command\"
"
  [ "$Sharecgroup" = "no" ] && [ "$Cgroupversion" = "v1" ] && {
    echo "
      debugnote \"DBus: \$Name: Removing \$Service\"
      rm \"\$Service\"
      echo \"\$Command\" | grep -q elogind && {
        note 'Found login service elogind in container
  and cgroup v1 on host.
  If you want to use elogind in container, enable option --sharecgroup.'
      }
"
  }
  echo "
    ;;
  esac
done
"
  return 0
}
rootrc_prepare_init_openrc() {
  echo "
# --init=openrc

# Tell openrc that it runs in a container
sed -e 's/#rc_sys=\"\"/rc_sys=\"$Backend\"/g' -i /etc/rc.conf

# Create and enable x11docker service containing container command
printf \"#!/sbin/openrc-run
name=x11docker
depend() {
  after *
}
start() {
  ebegin 'Starting x11docker-agetty'
  /usr/local/bin/x11docker-agetty
  openrc-shutdown --poweroff 0
  shutdown 0
  halt
  halt -f
  eend \$?
}
\" > /etc/init.d/x11docker.service
chmod +x /etc/init.d/x11docker.service
rc-update add x11docker.service default

# DBus service
[ -e /etc/init.d/dbus ] || echo '#!/sbin/openrc-run
start() {
  ebegin \"Starting D-BUS system messagebus\"
  /usr/bin/dbus-uuidgen --ensure=/etc/machine-id
  mkdir -p /var/run/dbus
  start-stop-daemon --start --pidfile /var/run/dbus.pid --exec /usr/bin/dbus-daemon -- --system
  eend \$?
}
stop() {
  ebegin \"Stopping D-BUS system messagebus\"
  start-stop-daemon --stop --pidfile /var/run/dbus.pid
  retval=\$?
  eend \${retval}
  [ -S /var/run/dbus/system_bus_socket ] && rm -f /var/run/dbus/system_bus_socket
  return \${retval}
}
reload() {
  ebegin \"Reloading D-BUS messagebus config\"
  /usr/bin/dbus-send --print-reply --system --type=method_call --dest=org.freedesktop.DBus / org.freedesktop.DBus.ReloadConfig > /dev/null
  retval=\$?
  eend \${retval}
  return \${retval}
}
' >/etc/init.d/dbus && chmod +x /etc/init.d/dbus
verbose 'DBus: enabling dbus service'
rc-update add dbus default
"
  return 0
}
rootrc_prepare_init_runit() {
  echo "
# --init=runit

# create and enable x11docker service containing container command
mkdir -p /etc/sv/x11docker
mkdir -p /etc/runit/runsvdir/default
mkdir -p /etc/runit/1.d
mkdir -p /service

echo \"#! /bin/sh
$(declare -f mysleep)
waitforservice() {
  Service=\\\$1
  [ \\\"\\\$(sv check \\\$Service | cut -d: -f1)\\\" = 'ok' ] && {
    echo \"x11docker: waiting for service \\\$Service ...\"
    for Count in $(seq -s' ' 20); do
      [ \\\"\\\$(sv status \\\$Service | cut -d: -f1)\\\" = 'down' ] && mysleep 0.2 || break
    done
  }
}
# make stderr visible
exec 2>&1
# wait for all other services
echo 'Content of /etc/runit/runsvdir/default:'
ls -la /etc/runit/runsvdir/default/*
for Service in /etc/runit/runsvdir/default/* ; do waitforservice \\\$Service ;done
echo 'Current status of runit services:'
for Service in /etc/runit/runsvdir/default/* ; do sv status      \\\$Service ;done
/usr/local/bin/x11docker-agetty
\" > /etc/sv/x11docker/run

chmod +x /etc/sv/x11docker/run

echo \"#! /bin/sh
sv down x11docker
runit-init 0
init 0
shutdown -h 0
halt
\" > /etc/sv/x11docker/finish

chmod +x /etc/sv/x11docker/finish

ln -s /etc/sv/x11docker /etc/runit/runsvdir/default #void
ln -s /etc/sv/x11docker /service                    #alpine

[ -e /etc/runit/1 ] || echo '#!/usr/bin/env sh
set -eu
chmod 100 /etc/runit/stopit
/bin/run-parts --exit-on-error /etc/runit/1.d || exit 100
' >/etc/runit/1

chmod +x /etc/runit/1

[ -e /etc/runit/2 ] || echo '#!/usr/bin/env sh
set -eu
runsvdir -P /service \"log: ..................................................................\"
' >/etc/runit/2

chmod +x /etc/runit/2

[ -e /etc/runit/3 ] || echo \"#!/usr/bin/env sh
set -eu
exec 2>&1
echo \"Waiting for services to stop...\"
sv -w196 force-stop /service/*
sv exit /service/*
# kill any other processes still running in the container
for ORPHAN_PID in \$(ps --no-headers -eo \"%p,\" -o stat | tr -d \" \" | grep \"Z\" | cut -d, -f1); do
    timeout 5 /bin/sh -c \"kill \$ORPHAN_PID && wait \$ORPHAN_PID || kill -9 \$ORPHAN_PID\"
done
\" >/etc/runit/3

chmod +x /etc/runit/3

[ -e /etc/sv/dbus ] || {
  mkdir -p /etc/sv/dbus
  echo '#!/bin/sh
[ ! -d /run/dbus ] && install -m755 -g 22 -o 22 -d /run/dbus
exec dbus-daemon --system --nofork --nopidfile
' >/etc/sv/dbus/run

  echo '#!/bin/sh
exec dbus-send --system / org.freedesktop.DBus.Peer.Ping > /dev/null 2> /dev/null
' >/etc/sv/dbus/check
  chmod +x /etc/sv/dbus/run /etc/sv/dbus/check
}
verbose 'DBus: enabling dbus service'
ln -s /etc/sv/dbus  /etc/runit/runsvdir/default  # void
ln -s /etc/sv/dbus  /service                     # alpine

touch /etc/runit/stopit
"
  return 0
}
rootrc_prepare_init_systemd() {
  echo "
# --init=systemd

# remove failing and annoying services
Unservicelist='
apt-daily.service
apt-daily.timer
apt-daily-upgrade.service
apt-daily-upgrade.timer
bluetooth.service
cgproxy.service
deepin-anything-monitor.service
deepin-sync-daemon.service
display-manager.service
fprintd.service
gdm3.service
gvfs-udisks2-volume-monitor.service
hwclock_stop.service
lastore-daemon.service
lastore-update-metadata-info.service
lightdm.service
NetworkManager.service
plymouth-quit.service
plymouth-quit-wait.service
plymouth-read-write.service
plymouth-start.service
rtkit-daemon.service
sddm.service
systemd-localed.service
systemd-hostnamed.service
tracker-extract.service
tracker-miner-fs.service
tracker-store.service
tracker-writeback.service
udisks2.service
upower.service
'
for Service in \$(find /lib/systemd/system/* /usr/lib/systemd/user/* /etc/systemd/system/* /etc/systemd/user/*) ; do
  echo \"\$Unservicelist\" | grep -q \"\$(basename \$Service)\" && {
    debugnote \"--init=systemd: Removing \$Service\"
    rm \$Service
  }
done

# Fix for Gnome 3
sed -i 's/ProtectHostname=yes/ProtectHostname=no/' /lib/systemd/system/systemd-logind.service

# create systemd units for x11docker

mkdir -p /etc/systemd/system.conf.d
echo \"[Manager]
DefaultEnvironment=$(while read -r Line; do echo -n "$Line " ; done < <(store_runoption dump env))
\" > /etc/systemd/system.conf.d/x11docker.environment.conf

echo \"[Unit]
Description=x11docker target
Wants=multi-user.target
After=multi-user.target
[Install]
Also=console-getty.service
Also=x11docker-watch.service
Also=x11docker-journal.service
\" > /etc/systemd/system/x11docker.target

echo \"[Unit]
Description=x11docker agetty service
# initiate console
# runs x11docker-agetty->x11docker-su or login->containerrc->cmdrc
Wants=multi-user.target
Wants=x11docker-watch.service
Wants=x11docker-journal.service
Wants=dbus.service
After=systemd-user-sessions.service
After=rc-local.service getty-pre.target
Before=getty.target
[Service]
ExecStart=/usr/local/bin/x11docker-agetty
StandardInput=tty
StandardOutput=tty
Type=idle
UtmpIdentifier=cons
TTYPath=/dev/console
TTYReset=yes
TTYVHangup=yes
KillMode=process
IgnoreSIGPIPE=no
SendSIGHUP=yes
[Install]
WantedBy=x11docker.target
WantedBy=getty.target
WantedBy=multi-user.target
\" > /lib/systemd/system/console-getty.service

echo \"[Unit]
Description=x11docker watch service
# Watches for end of containerrc and initiates shutdown
[Service]
Type=simple
ExecStart=/bin/sh -c 'while sleep 1; do systemctl is-active console-getty >/dev/null || { echo timetosaygoodbye >>$(convertpath share "$Timetosaygoodbyefile") ; systemctl halt ; } ; [ -s $(convertpath share "$Timetosaygoodbyefile") ] && systemctl halt ; done'
[Install]
WantedBy=x11docker.target
\" > /etc/systemd/system/x11docker-watch.service

echo \"[Unit]
Description=x11docker containerrc service
[Service]
Type=simple
ExecStart=sh $(convertpath share "$Containerrc")
[Install]
WantedBy=default.target
\" > /etc/systemd/user/x11docker-containerrc.service

echo \"[Unit]
Description=x11docker journal log service
# get systemd log to transfer it into x11docker.log
[Service]
Type=simple
ExecStart=/bin/sh -c '/bin/journalctl --follow --no-tail >> $(convertpath share "$Systemdjournallogfile") 2>&1'
[Install]
WantedBy=x11docker.target
\" > /etc/systemd/system/x11docker-journal.service

# enable x11docker CMD service
systemctl unmask console-getty.service
systemctl enable console-getty.service
systemctl enable x11docker-journal.service

# enable logind service
systemctl unmask systemd-logind
systemctl enable systemd-logind

# enable DBus service
systemctl unmask dbus
systemctl enable dbus
"
  return 0
}
rootrc_prepare_init_sysvinit() {
  echo "
# --init=sysvinit
# Adding x11docker start command to rc.local
sed -i '/exit 0/d' /etc/rc.local

echo \"
/usr/local/bin/x11docker-agetty || echo \\\"x11docker: Exit code of x11docker-agetty: \\\$?\\\"
echo 'x11docker: rc.local sends shutdown -h now'
shutdown -h now
exit 0
\" >> /etc/rc.local

chmod +x /etc/rc.local

# DBus service
echo '#!/bin/sh
### BEGIN INIT INFO
# Provides:          dbus
# Required-Start:    \$remote_fs \$syslog
# Required-Stop:     \$remote_fs \$syslog
# Default-Start:     2 3 4 5
# Default-Stop:
# Short-Description: D-Bus systemwide message bus
# Description:       D-Bus is a simple interprocess messaging system, used
#                    for sending messages between applications.
### END INIT INFO
# -*- coding: utf-8 -*-
# Debian init.d script for D-BUS
# Copyright © 2003 Colin Walters <walters@debian.org>
# Copyright © 2005 Sjoerd Simons <sjoerd@debian.org>
#
DAEMON=/usr/bin/dbus-daemon
UUIDGEN=/usr/bin/dbus-uuidgen
UUIDGEN_OPTS=--ensure
NAME=dbus
DAEMONUSER=messagebus
PIDDIR=/var/run/dbus
PIDFILE=\"\$PIDDIR/pid\"
DESC=\"system message bus\"
#
test -x \$DAEMON || exit 1
. /lib/lsb/init-functions
# Source defaults file; edit that file to configure this script.
PARAMS=""
if [ -e /etc/default/dbus ]; then
  . /etc/default/dbus
fi
create_machineid() {
  # Create machine-id file
  if [ -x \$UUIDGEN ]; then
    \$UUIDGEN \$UUIDGEN_OPTS
  fi
}
start_it_up() {
  [ -d \$PIDDIR ] || {
    mkdir -p \$PIDDIR
    chown \$DAEMONUSER \$PIDDIR
    chgrp \$DAEMONUSER \$PIDDIR
  }
  mountpoint -q /proc/ || {
    log_failure_msg \"Cannot start \$DESC - /proc is not mounted\"
    return 1
  }
  [ -e \$PIDFILE ] && {
    \$0 status > /dev/null && {
      log_success_msg \"\$DESC already started; not starting.\"
      return 0
    }
    log_success_msg \"Removing stale PID file \$PIDFILE.\"
    rm -f \$PIDFILE
  }
  create_machineid
  log_daemon_msg \"Starting \$DESC\" \"\$NAME\"
  start-stop-daemon --start --quiet --pidfile \$PIDFILE --exec \$DAEMON -- --system \$PARAMS
  log_end_msg \$?
}
shut_it_down() {
  log_daemon_msg \"Stopping \$DESC\" \"\$NAME\"
  start-stop-daemon --stop --retry 5 --quiet --oknodo --pidfile \$PIDFILE --user \$DAEMONUSER
  log_end_msg \$?
  rm -f \$PIDFILE
}
reload_it() {
  create_machineid
  log_action_begin_msg \"Reloading \$DESC config\"
  dbus-send --print-reply --system --type=method_call --dest=org.freedesktop.DBus / org.freedesktop.DBus.ReloadConfig > /dev/null
  log_action_end_msg \$?
}
case \$1 in
  start) start_it_up ;;
  stop)  shut_it_down ;;
  reload|force-reload) reload_it ;;
  restart)
    shut_it_down
    start_it_up
  ;;
  status) status_of_proc -p \$PIDFILE \$DAEMON \$NAME && exit 0 || exit \$? ;;
esac
' > /etc/init.d/dbus
chmod +x /etc/init.d/dbus
"
  return 0
}
rootrc_setup_user() {
  local Line

  echo "
# Set up container user
"
  case "$Containerusershell" in
    auto) echo "bash --version >/dev/null 2>&1 && Containerusershell=/bin/bash || Containerusershell=/bin/sh" ;;
    *)    echo "Containerusershell='$Containerusershell'" ;;
  esac

  echo "
# /etc/passwd
Containeruserentry=\"\$Containeruser:x:$Containeruseruid:$Containerusergid:\$Containeruser,,,:\$Containeruserhome:\$Containerusershell\"
debugnote \"containerrootrc: \$Containeruserentry\"

# Disable possible /etc/shadow passwords for other users
# Delete root user
# Delete possibly existing user with same uid
sed -i 's%:x:%:-:% ; /:0:0:/d ; /:$Containeruseruid:/d' /etc/passwd

echo \"\$Containeruserentry\" >> /etc/passwd
echo \"root:-:0:0:root:/root:\$Containerusershell\" >> /etc/passwd

# Create password entry for container user in /etc/shadow
rm -f -v /etc/shadow || warning 'Cannot change /etc/shadow. That may be a security risk.'
echo \"\$Containeruser:$Containeruserpassword:17293:0:99999:7:::\" > /etc/shadow
"
  case "$Sudouser" in
    "")  echo "echo 'root:*:17219:0:99999:7:::' >> /etc/shadow" ;;
    *)   echo "echo 'root:$Containeruserpassword:17219:0:99999:7:::' >> /etc/shadow  # with option --sudouser, set root password 'x11docker'"
         echo "sed -i 's%root:-:%root:x:%' /etc/passwd                               # allow password"
    ;;
  esac

  echo "
chmod 640 /etc/shadow   # can fail depending on available capabilities

# sudo configuration
# Create /etc/sudoers, delete /etc/sudoers.d. Overwrite possible sudo setups in image.
[ -e /etc/sudoers.d ] && rm -f -v -R /etc/sudoers.d
[ -e /etc/sudoers ]   && rm -f -v /etc/sudoers
echo '# /etc/sudoers created by x11docker' > /etc/sudoers
echo 'Defaults	env_reset'                >> /etc/sudoers
echo 'root ALL=(ALL) ALL'                 >> /etc/sudoers
"
  case "$Sudouser" in
    yes)      echo "echo '$Containeruser ALL=(ALL) ALL' >> /etc/sudoers" ;;
    nopasswd) echo "echo '$Containeruser ALL=(ALL) NOPASSWD: ALL' >> /etc/sudoers" ;;
  esac

  # try to disable possible custom PAM setups that could allow a switch to root in container ### FIXME maybe not foolproof
  [ -z "$Sudouser" ] && {
    echo "# /etc/pam.d
# Restrict PAM configuration of su and sudo
mkdir -p /etc/pam.d
[ -e /etc/pam.d/sudo ] && rm -f -v /etc/pam.d/sudo
case \"\$Containersystem\" in
  fedora)
    echo '#%PAM-1.0' > /etc/pam.d/su
    echo 'auth     sufficient pam_rootok.so'  >> /etc/pam.d/su
#    echo 'auth     substack system-auth'      >> /etc/pam.d/su
#    echo 'auth     include postlogin'         >> /etc/pam.d/su
    echo 'account  sufficient pam_succeed_if.so uid = 0 use_uid quiet'  >> /etc/pam.d/su
#    echo 'account  include system-auth'       >> /etc/pam.d/su
#    echo 'password include system-auth'       >> /etc/pam.d/su
    echo 'session  include system-auth'       >> /etc/pam.d/su
    echo 'session  include  postlogin'        >> /etc/pam.d/su
    echo 'session  optional pam_xauth.so'     >> /etc/pam.d/su
  ;;
  *)
    echo '#%PAM-1.0' > /etc/pam.d/su
    echo 'auth sufficient pam_rootok.so' >> /etc/pam.d/su  # allow root to switch user without a password
    echo '@include common-auth'          >> /etc/pam.d/su
    echo '@include common-account'       >> /etc/pam.d/su
    echo '@include common-session'       >> /etc/pam.d/su
  ;;
esac
"
  }

  echo "# /etc/group
sed -i \"s/\$Containeruser//g ; s/:,/:/g ; s/,\$//g\" /etc/group  # remove existing entries for user
"
  for Line in $Containerusergroups ; do
    echo "
Groupname=\"$(cat /etc/group 2>/dev/null | grep "^$Line" | cut -d: -f1)\"
Groupid=\"$(cat /etc/group   2>/dev/null | grep "^$Line" | cut -d: -f3)\"
[ \"\$Groupname\" ] || Groupname=\"\$(cat /etc/group | grep \"$Line\" | cut -d: -f1)\"
[ \"\$Groupid\" ]   || Groupid=\"\$(cat /etc/group | grep \"$Line\" | cut -d: -f3)\"
[ \"\$Groupname\" ] && {
  Entry=\"\$(grep \$Groupname /etc/group)\"
  Entry=\"\$(echo \"\$Entry\" | cut -d: -f3-)\"
  [ -z \"\$Entry\" ] && Entry=\"\$Groupid:\"
  Entry=\"\$Groupname:x:\$Entry,\$Containeruser\"
  Entry=\"\$(echo \"\$Entry\" | sed 's/:,/:/g')\"
  sed -i \"/^\$Groupname:/d\" /etc/group
  echo \"\$Entry\" >> /etc/group
} || {
  note \"Failed to add container user to group '$Line'.\"
}
# Create user group entry (and delete possibly existing same gid)
sed -i '/:$Containerusergid:/d' /etc/group
echo \"$Containerusergroup:x:$Containerusergid:\" >> /etc/group
"
  done

  return 0
}
rootrc_timezone() {
  echo "
# Time zone
[ ! -d /usr/share/zoneinfo ] && [ \"\$Containerlibc\" = \"$Hostlibc\" ] && {
  mkdir -p $(dirname "$Hostlocaltimefile")
  cp '$(convertpath share "$Containerlocaltimefile")' '$Hostlocaltimefile'
}
[ -e '$Hostlocaltimefile' ] && ln -f -s '$Hostlocaltimefile' /etc/localtime
"
  return 0
}
rootrc_xrandr() {
  echo "
# workaround: autostart of xrandr for some desktops like deepin, cinnamon and gnome to fix wrong autoresize
echo '#! /bin/sh
Output=\$(xrandr | grep \" connected\" | cut -d\" \" -f1)
Mode=$Screensize
xrandr --output \$Output --mode \$Mode
' > /usr/local/bin/x11docker-xrandr

chmod +x /usr/local/bin/x11docker-xrandr
mkdir -p /etc/xdg/autostart

echo '[Desktop Entry]
Encoding=UTF-8
Version=0.9.4
Type=Application
Name=x11docker-xrandr
Comment=
Exec=/usr/local/bin/x11docker-xrandr
' > /etc/xdg/autostart/x11docker-xrandr.desktop

"
  return 0
}

#### final startup routines
start_container() {             # docker run
  # create containerrc -> runs as unprivileged user in container
  # check and set up cgroup on host for systemd or elogind
  # run docker
  local Containerid= Containerip= Containerinspect=
  local Failure= Pid1pid=

#  [ "$Winsubsystem" = "MSYS2" ] && {  ### FIXME check if needed
#    # avoid path conversion in MSYS2 commands
#    export MSYS2_ARG_CONV_EXCL='*'
#  }

  Backendcommand="$(tr -d '\\\n' <<< "$Backendcommand")"
  debugnote "start_container(): Running image"

  case "$Interactive" in
    no)
      case "$Backend" in
        host)
          bash "$Containerrc" & Pid1pid=$!
        ;;
        proot|chroot)
          eval $Backendcommand & Pid1pid=$!
        ;;
        docker|podman|nerdctl)
          #read Containerid < <(eval $Backendcommand 2>> $Containerlogfile | rmcr)
          eval $Backendcommand >> "$Containerlogfile" 2>&1 ||: &
        ;;
      esac
    ;;
    yes)
      case "$Backend" in
        host)
          bash "$Containerrc" <&0 & Pid1pid=$!
        ;;
        proot|chroot)
          eval $Backendcommand <&0 & Pid1pid=$!
        ;;
        docker|podman|nerdctl)
          [ "$Winpty" ] && {
            $Winpty bash "$Backendcommandfile" <&0 &
          } || {
            eval $Backendcommand <&0 &
          }
        ;;
      esac
    ;;
  esac

  case "$Backend" in
    proot|chroot|host) ;;
    docker|podman|nerdctl)
      # Wait for container to be ready
      for ((Count=1 ; Count<=40 ; Count++)); do
        $Backendbin exec "$Containername" sh -c ":" 2>&1 | rmcr >> "$Containerlogfile" && {
          debugnote 'start_container(): Container is up and running.'
          break
        } || {
          debugnote "start_container(): Container not ready on ${Count}. attempt, trying again."
        }
        rocknroll || break
        mysleep 0.1
      done

      # store stdout and stderr
      [ "$Containersetup" = "no" ] && {
        # Store container output separated for stdout and stderr
        $Backendbin logs -f "$Containername" 1>> "$Cmdstdoutlogfile" 2>> "$Cmdstderrlogfile" &
        storepid $! dockerlogs
      }

      # Wait for pid 1 in container
      for ((Count=1 ; Count<=40 ; Count++)); do
        Containerinspect="$($Backendbin inspect "$Containername" 2>> "$Containerlogfile" | rmcr)"
        [ "$Containerinspect" != "[]" ] && Pid1pid="$(parse_inspect "$Containerinspect" "State" "Pid")"
        debugnote "start_container(): $Count. check for PID 1: $Pid1pid"
        case "$Mobyvm" in
          no)  checkpid "$Pid1pid" && break ;;
          yes) [ "$Pid1pid" ] && [ "$Pid1pid" != "0" ] && break
        esac
        rocknroll || break
        mysleep 0.1
      done
      [ "$Pid1pid" = "0" ] && Pid1pid=""

      [ -z "$Pid1pid" ] && error "start_container(): Did not receive PID of PID1 in container.
  Maybe the container immediately stopped for unknown reasons.
  Just in case, check if host and image architecture are compatible:
  Host architecture: $Hostarchitecture, image architecture: $Imagearchitecture.
  Output of \"$Backend ps | grep x11docker\":
$Backendbin ps | grep x11docker)

  Content of container log:
$(rmcr < "$Containerlogfile" | uniq )"

      # Get IP of container
      Containerip="$(parse_inspect "$Containerinspect" "NetworkSettings" "IPAddress")"
      storeinfo "containerip=$Containerip"
      # Get ID of container
      Containerid="$(parse_inspect "$Containerinspect" "Id")"
      storeinfo "containerid=$Containerid"

      # --init=systemd cgroupv2
      [ "$Remountcgroup" = "yes" ] && {
        # Compare https://github.com/mviereck/x11docker/issues/349
        Remountcommand="$Backendbin run --rm --user root"
        Remountcommand="$Remountcommand --cap-add SYS_ADMIN --cap-add=SYS_PTRACE --security-opt apparmor=unconfined"
        Remountcommand="$Remountcommand --pid=container:$Containerid"
        Remountcommand="$Remountcommand --entrypoint /usr/bin/env"
        [ "$Runtime" ] && Remountcommand="$Remountcommand --runtime $Runtime"
        Remountcommand="$Remountcommand -- $Imagename"
        Remountcommand="$Remountcommand nsenter -t 1 -m -C -p /bin/sh -c '"
        Remountcommand="$Remountcommand         set -x ; "
        Remountcommand="$Remountcommand         findmnt /sys/fs/cgroup -O rw || mount -v -o remount,rw /sys/fs/cgroup/ ; "
        Remountcommand="$Remountcommand         findmnt /sys/fs/cgroup -O rw || echo \"ERROR: cgroup mount failed.:NOTE\" >>/x11docker/message.fifo ; "
        Remountcommand="$Remountcommand         touch /nsenter_is_ready'"
        debugnote "start_container(): Remounting /sys/fs/cgroup to :rw with nsenter:
  $Remountcommand"
        echo "cgroup mount with nsenter:" >> "$Containerlogfile"
        eval $Remountcommand >> "$Containerlogfile" 2>&1 || {
          note "--init=$Initsystem: Failed to remount /sys/fs/cgroup in container.
  Startup of systemd might fail.
  nsenter command:
    $Remountcommand
  Last lines of log:
$(tail "$Containerlogfile")"
        }
      }
    ;;
  esac
  storeinfo "pid1pid=$Pid1pid"

  # Check log for startup failure
  Failure="$(rmcr < "$Containerlogfile" | grep -v grep | grep -E 'OCI runtime exec' ||:)"
  [ "$Failure" ] && {
    echo "$Failure" >> "$Containerlogfile"
    error "start_container(): Got error message from backend $Backend:
$Failure

  Last lines of logfile:
$(tail "$Containerlogfile")"
  }

  # start containerrootrc
  [ "$Switchcontaineruser" = "no" ] && [ "$Containersetup" = "yes" ] && {
    debugnote "start_container(): Starting containerrootrc with $Backend exec"
    # copy containerrootrc inside of container to avoid possible noexec of host home.
    case "$Backend" in
      proot|chroot) ### FIXME currently with Switchcontaineruser=yes only
      ;;
      host) ;; # no containerrootrc
      docker|podman|nerdctl)
        $Backendbin exec "$Containername" sh -c "cp $(convertpath share "$Containerrootrc") /tmp/containerrootrc ; chmod 644 /tmp/containerrootrc" 2>&1 | rmcr >> "$Containerlogfile"
        # run container root setup. containerrc will wait until setup script is ready.
        $Backendbin exec -u root "$Containername" /bin/sh /tmp/containerrootrc 2>&1 | rmcr >> "$Containerlogfile"
      ;;
    esac
  }

  return 0
}
start_compositor() {            # start Wayland compositor Weston or KWin
  local Compositorkeyword Command
  Command="$(tr -d '\\\n' <<< "$Compositorcommand")"
  case "$Xserver" in
    --weston|--weston-xwayland|--xpra-xwayland|--xpra2-xwayland) Compositorkeyword="weston-desktop-shell" ;;
#    --kwin|--kwin-xwayland)                                      Compositorkeyword="CreateListener" ;;
    --kwin|--kwin-xwayland)                                      Compositorkeyword="XKEYBOARD" ;;
  esac

  case "$Xcontainer" in
    yes)
      $Backendbin exec --detach "$Xcontainername" sh -c "$Command >> '$Compositorlogfile'  2>&1" 2>> "$Compositorlogfile"
      Compositorpid="$(ps aux | grep "$(cut -c1-50 <<< "$Command")" | grep -v grep | awk '{print $2}')"
    ;;
    no)
      unpriv "$Command  >> '$Compositorlogfile'  2>&1 & echo compositorpid=\$! >> '$Storeinfofile'"
      Compositorpid="$(storeinfo dump compositorpid)"
    ;;
  esac
  storeinfo "compositorpid=$Compositorpid"
  waitforlogentry "start_compositor()" "$Compositorlogfile" "$Compositorkeyword" "$Compositorerrorcodes"
  setonwatchpidlist "$(storeinfo dump compositorpid)" compositor

  case "$Xserver" in
    --xpra-xwayland|--xpra2-xwayland)  # hide weston window
      unpriv "xdotool windowunmap 0x$(printf '%x\n' $(grep 'window id' "$Compositorlogfile" | rev | cut -d' ' -f1 | rev))" ;;
  esac
  return 0
}
start_xcontainer() {            # --xc
  local Containerinspect Xpid1pid

  Xcontainercommand="$(tr -d '\\\n' <<< "$Xcontainercommand")"
  read Xcontainerid< <($Xcontainercommand 2>> "$Xinitlogfile")
  storeinfo "Xcontainerid=$Xcontainerid"

  Containerinspect="$($Xcontainerbackend inspect "$Xcontainername" 2>> "$Xinitlogfile" | rmcr)"
  [ "$Containerinspect" != "[]" ] && {
    Xcontainerip="$(parse_inspect "$Containerinspect" "NetworkSettings" "IPAddress")"
    storeinfo "Xcontainerip=$Xcontainerip"
    Xpid1pid="$(parse_inspect "$Containerinspect" "State" "Pid")"
    setonwatchpidlist "$Xpid1pid" "Xcontainerpid1"
  } || {
    error "--xc: Startup of x11docker/xserver failed.
  Last lines of xinit log:
$(tail "$Xinitlogfile")"
    return 1
  }

  [ "$Nvidiainstallerfile" ] && {
    note "Installing NVIDIA driver in X container."
    docker exec --tty --privileged --user=root "$Xcontainername" bash "$Sharefolder/nvidia_installer" >> "$Xinitlogfile" 2>&1 || note 'ERROR: NVIDIA driver installation in X container failed.'
  }
#  watchpid CONTAINER$Xcontainername
  return 0
}
start_xserver() {               # start X server
  local Exitcode= Xcontainerexitcode=
  Xcommand="$(tr -d '\\\n' <<< "$Xcommand")"
  case "$Xserver" in
    --xpra*|--xephyr|--xvfb|--nxagent|--xwayland|--weston-xwayland|--kwin-xwayland|--xwin)
      case "$Xcontainer" in
        no)  unpriv                         "env WAYLAND_DISPLAY='$Newwaylandsocket' xinit '$Xinitrc' -- $Xcommand  >> '$Xinitlogfile'  2>&1 " ;;
        yes)
          $Xcontainerbackend exec "$Xcontainername" env WAYLAND_DISPLAY="$Newwaylandsocket" xinit "$Xinitrc" -- $Xcommand  >> "$Xinitlogfile"  2>&1 || {
            Xcontainerexitcode=$?
            case "$Xcontainerexitcode" in
              137) Exitcode="0" ;; # docker error indicating that xinit did not shut down fast enough on SIGTERM. Ignore it.
              *)   Exitcode="$Xcontainerexitcode" ;;
            esac
          }
        ;;
      esac
    ;;
    --xorg)
      case "$Xlegacywrapper" in
        yes) unpriv                                                                  "xinit '$Xinitrc' -- $Xcommand  >> '$Xinitlogfile'  2>&1 " ;;
        no)  eval                                                                    "xinit '$Xinitrc' -- $Xcommand  >> '$Xinitlogfile'  2>&1 " ;;
      esac
    ;;
    --hostdisplay|--hostwayland|--weston|--kwin|--tty)
      unpriv                                                                         "bash  '$Xinitrc'               >> '$Xinitlogfile'  2>&1 " ;;
    --runx) unpriv                                                      "$Xcommand -- bash  '$Xinitrc'               >> '$Xinitlogfile'  2>&1 " ;;
  esac
  Exitcode="$?"
  [ -n "$Xcontainerexitcode" ] && Exitcode="$Xcontainerexitcode"
  [ "$Exitcode" != 0 ] && rocknroll && note "X server $Xserver returned error code $Exitcode.
  Last lines of xinit logfile:
$(tail "$Xinitlogfile")

  $( [ -s "$Compositorlogfile" ] && echo "Last lines of compositor log:
$(tail "$Compositorlogfile")")"
  return "${Exitcode:-0}"
}
start_xpra() {                  # options --xpra / --xpra-xwayland: start and watch xpra server and xpra client
  local Xpraserverpid Xpraclientpid Xpraenv

  Xpraenv="  $Xprashm \\
  NO_AT_BRIDGE=1 \\
  XPRA_EXPORT_ICON_DATA=0 \\
  XPRA_EXPORT_XDG_MENU_DATA=0 \\
  XPRA_ICON_OVERLAY=0 \\
  XPRA_MENU_ICONS=0 \\
  XPRA_UINPUT=0 \\
  XPRA_VAAPI=0 \\
  XPRA_XDG_EXPORT_ICONS=0 \\
  XPRA_XDG_LOAD_GLOB=0"

  # xpra server
  Xpraservercommand="env XAUTHORITY='$Xclientcookie' \\
  GDK_BACKEND=x11 \\
$Xpraenv $Xpraservercommand"
  debugnote "Running xpra server:
$Xpraservercommand"
  echo "x11docker [$(timestamp)]: Starting Xpra server" >> "$Xpraserverlogfile"
  case "$Xcontainer" in
    no)
      unpriv "$Xpraservercommand ||:" >> "$Xpraserverlogfile" 2>&1 &
      #Xpraserverpid=$!
    ;;
    yes)
      $Xcontainerbackend exec --detach "$Xcontainername" sh -c "$(tr -d '\\\n' <<< "$Xpraservercommand") >> '$Xpraserverlogfile' 2>&1" >> "$Xpraserverlogfile" 2>&1
      #Xpraserverpid="$(ps aux | grep "/usr/bin/xpra start $Newdisplay" | grep -v grep | awk '{print $2}')"
    ;;
  esac

  ### FIXME needs version check. release number is not reliable.
  # https://github.com/Xpra-org/xpra/issues/2346
  #verlt "$Xprarelease" "r23060" && waitforlogentry "xpra server" $Xpraserverlogfile 'xpra is ready'
  #rocknroll || return 64

  # xpra client
  Xpraclientcommand="env $Hostxenv \\
$Xpraenv $Xpraclientcommand"
  debugnote "Running xpra client:
$Xpraclientcommand"
  echo "x11docker [$(timestamp)]: Starting Xpra client" >> "$Xpraclientlogfile"
  [ "$Xcontainer" = "yes" ] && [ "$Xserver" != "--xpra2" ] && [ "$Xserver" != "--xpra2-xwayland" ] && {
    $Xcontainerbackend exec --detach "$Xcontainername" sh -c "$(tr -d '\\\n' <<< "$Xpraclientcommand") >> '$Xpraclientlogfile' 2>&1" >> "$Xpraclientlogfile" 2>&1
    :
  } || {
    unpriv "$Xpraclientcommand ||:" >> "$Xpraclientlogfile" 2>&1 &
  }

  # get pids
  sleep 1   # avoid race condition not finding the process
  Xpraserverpid="$(ps aux | grep -E "/usr/bin/xpra start $Newdisplay|/usr/bin/xpra start-desktop $Newdisplay" | grep -v grep | awk '{print $2}')"
  storepid "$Xpraserverpid" xpraserver
  Xpraclientpid="$(ps aux | grep "/usr/bin/xpra attach $Newdisplay" | grep -v grep | awk '{print $2}')"
  storepid "$Xpraclientpid" xpraclient

  # catch possible xpra crashes
  while rocknroll; do
    ps -p "$Xpraserverpid" >/dev/null || { debugnote "xpra server terminated" ; break ; }
    ps -p "$Xpraclientpid" >/dev/null || { debugnote "xpra client terminated" ; break ; }
    sleep 1
  done
  sleep 3 && rocknroll && note "Option $Xserver: xpra terminated unexpectedly.
  Last lines of xpra server log: $(pspid "$Xpraserverpid")
$(tail "$Xpraserverlogfile")
---------------------------------
  Last lines of xpra client log: $(pspid "$Xpraclientpid")
$(tail "$Xpraclientlogfile")"
  saygoodbye xpra

  return 0
}
start_pulseaudiotcp() {         # option --pulseaudio=tcp: load Pulseaudio TCP module authenticated with container IP
  local Containerip
  Containerip="$(storeinfo dump containerip)"
  Pulseaudiomoduleid="$(unpriv "pactl load-module module-native-protocol-tcp  port=$Pulseaudioport auth-ip-acl=${Containerip:-"127.0.0.1"}" )"
  [ "$Pulseaudiomoduleid" ] && {
    storeinfo "pulseaudiomoduleid=$Pulseaudiomoduleid"
  } || note "Option --pulseaudio: command pactl failed.
  Is pulseaudio running at all on your host?
  You can try option --alsa instead."
  return 0
}

#### main init routines
check_backend() {               # option --backend
  case "$Mobyvm" in
    no)
      Backendbin="$(command -v "$Backend")"
    ;;
    yes)
      case "$Backend" in
        docker|host) ;;
        *) 
          error "Option --backend=$Backend is not supported on MS Windows yet.
  If you need this, ask for support at https://github.com/mviereck/x11docker"
          return 1
        ;;
      esac
      Backendbin="docker.exe"
      command -v "$Backendbin" >/dev/null || {
        PATH="${PATH:-}:$(convertpath subsystem "C:/Program Files/docker"):$(convertpath subsystem "C:/Program Files/Docker/Docker/resources/bin")"
        export PATH
      }
    ;;
  esac

  case "$Backend" in
    docker|podman|nerdctl|proot|chroot)
      [ -z "$Backendbin" ] && error "Option --backend=$Backend: No binary found for $Backend."
    ;;
    host) Backendbin="" ;;
  esac

  # rootful or rootless
  case "$Backend" in
    docker)
      grep -q "/run/user/" <<< "${DOCKER_HOST:-}" && Rootlessbackend="yes"
    ;;
    podman|nerdctl|proot)
      [ "$(id -u)" != "0" ] && Rootlessbackend="yes"
    ;;
  esac
  
  case "$Backend" in
    docker|podman|nerdctl)
      # Check whether docker daemon is running, get docker info
      Backendinfo="$($Backendbin info --format='{{json .}}' 2>>$Containerlogfile)" || error "'$Backend info' failed.
  If using docker: Is docker daemon running at all?
  Try to start docker daemon with 'systemctl start docker'.
  Last lines of log:
$(rmcr < "$Containerlogfile" | tail)"
      check_runtime
    ;;
  esac
  
  case "$Backend" in
    docker|podman|nerdctl|proot|chroot)
      [ "${Imagename:0:1}" = "/" ] && {
        Rootfs="$Imagename"
        Imagename="$(basename "$Rootfs")"
      }
      Imagebasename="$(echo "$Imagename" | tr / - | cut -d: -f1)"
      Codename="$Imagename $Containercommand"
      verbose "Image name: $Imagename
  Container command: $Containercommand"
    ;;
    host)
      Containercommand="$Imagename $Containercommand"
      [ "$Customdockeroptions" ] && error "Option --backend=$Backend does not take CUSTOM_RUN_OPTIONS.
  If your command args contain ' -- ', run like
  x11docker --backend=$Backend [OPTIONS] -- -- COMMAND -- ARGS"
      Imagename=""
      Imagebasename="$(basename "$Containercommand" | cut -d' ' -f1)"
      Codename="$Imagebasename"
    ;;
  esac
  Codename="$(unspecialstring "$Codename" | cut -c1-40)"
  Codename="${Codename:-noname}"
  Imagebasename="$(unspecialstring "$Imagebasename")"  # must be - for backwards compatibility of --home
  Imagebasename="${Imagebasename:-noname}"

  case "$Backend" in
    docker) ;;
    podman)
      # /proc/sys/kernel/unprivileged_userns_clone might exist on debian only.
      # https://github.com/mviereck/x11docker/issues/255#issuecomment-758014962
      [ "$(cat /proc/sys/kernel/unprivileged_userns_clone)" = "0" ] && error "Option --backend=podman: Linux kernel disallows
  unprivileged user namespace setup. Please run as root:
    sysctl -w kernel.unprivileged_userns_clone=1"
      store_runoption cap "CHOWN"
    ;;
    nerdctl)
      note "Option --backend=nerdctl: nerdctl only supports a subset
  of docker options. That limits support of x11docker features."
      Switchcontaineruser="yes"
      [ "$Capdropall" = "yes" ] && warning "Option --backend=nerdctl: x11docker
  will allow some user switching capabilities that would be dropped with
  other backends. (Because 'nerdctl --exec' does not support option --user.)
  Though, these are still within nerdctl default capabilities."
    ;;
    proot)
      warning "Option --backend=$Backend: Your host system is NOT PROTECTED.
  There is close to no isolation like a real container could provide.
  Effectively applications in proot have the same privileges as if
  they would have been installed and started directly on the host system."
      check_optionset "--backend=$Backend" "--cap-default --hostipc --ipc --limit --network --newprivileges --no-setup --runtime --sharecgroup --user" ||:
      check_optionset "--backend=$Backend" "--user" && error "--backend=$Backend: Option --user is not allowed.
  Option --hostuser might serve you."
      # set defaults. Some might look misleading just to avoid later messages, but not affecting the setup.
      Network="host"
      Shareipc="host"
      Createcontaineruser="yes"
      Containersetup="yes"
      Capdropall="yes"
      Allownewprivileges="no"
      Runtime=""
      Sharecgroup="no"
      Limitresources=""
      # needed
      Switchcontaineruser="yes"

      [ -z "$Rootfs" ] && [ -d "$Hosthomebasefolder/ROOTFS/$Imagebasename" ] && Rootfs="$Hosthomebasefolder/ROOTFS/$Imagebasename"
      [ -z "$Rootfs" ] && error "--backend=$Backend: Did not find a rootfs for $Imagename
  in $Hosthomebasefolder/ROOTFS/$Imagebasename"
      [ -z "$Containercommand" ] && [ -e "$Rootfs/start" ] && Containercommand="/bin/sh /start"
      [ -z "$Containercommand" ] && {
        [ -e "$Rootfs/bin/bash" ]                          && Containercommand="/bin/bash" || Containercommand="/bin/sh"
        [ "$Containerusershell" != "auto" ]                && Containercommand="$Containerusershell"
        note "--backend=$Backend: No command specified.
Fallback: using command '$Containercommand' and enabling option --interactive."
        check_fallback
        Interactive="yes"
      }
      [ "$Sudouser" ] && note "--backend=$Backend --sudouser: sudo fails in $Backend.
  Workaround: starting with user root.
  You can switch to user $Containeruser with 'su $Containeruser'.
  Note that root in $Backend is a fake and does not have real root privileges." ### FIXME chroot
    ;;
    host)
      warning "Option --backend=host provides no isolation at all.
  It only provides an X server for a host application."
      storeinfo "containerrootrc=ready"
      Sharefoldercontainer="$Sharefolder"
      # --dbus, --hostdbus, --init
      check_optionset "--backend=$Backend" "--alsa --cap-default --gpu --group-add --hostipc --ipc --limit --home --name --network --newprivileges --no-entrypoint --no-setup \
                       --printer --pulseaudio --runasroot --runtime --share --sharecgroup --sudouser --webcam" ||:
      check_optionset "--backend=$Backend" "--user" && error "--backend=$Backend: Option --user is not allowed.
  Option --hostuser might serve you."
      # set defaults. Some might look misleading just to avoid later messages, but not affecting the setup.
      Sharealsa="no"
      Capdropall="yes"
      Sharegpu="no"
      Shareipc="host"
      [ "$Langwunsch" ] && store_runoption env "LANG=$Langwunsch"
      Limitresources=""
      Network="host"
      Allownewprivileges="no"
      Noentrypoint=""
      Containersetup="yes"
      Sharecupsmode=""
      Pulseaudiomode=""
      Runasroot=""
      Runtime=""
      Sharevolumes=""
      Sharecgroup="no"
      Sudouser="no"
      Sharewebcam=""
    ;;
    chroot) ### FIXME
      warning "--backend=$Backend is highly experimental.
   Prefer to use another backend. Even 'proot' is more reliable.
   Isolation is close to not existant."
      [ -z "$Rootfs" ] && [ -d "$Hosthomebasefolder/ROOTFS/$Imagebasename" ] && Rootfs="$Hosthomebasefolder/ROOTFS/$Imagebasename"
      [ -z "$Rootfs" ] && error "--backend=$Backend: Did not find a rootfs for $Imagename
  in $Hosthomebasefolder/ROOTFS/$Imagebasename"
      [ -z "$Containercommand" ] && [ -e "$Rootfs/start" ] && Containercommand="/bin/sh /start"
      [ -z "$Containercommand" ] && {
        [ -e "$Rootfs/bin/bash" ]                          && Containercommand="/bin/bash" || Containercommand="/bin/sh"
        [ "$Containerusershell" != "auto" ]                && Containercommand="$Containerusershell"
        note "--backend=$Backend: No command specified.
Fallback: using command '$Containercommand' and enabling option --interactive."
        check_fallback
        Interactive="yes"
      }
      Switchcontaineruser="yes"
    ;;
  esac
  return 0
}
check_fallback() {              # --fallback
  # Option --fallback
  case "$Fallback" in
    no) error "Option --fallback=no: Fallbacks are disabled.
    x11docker cannot fulfill an option you have chosen, see message above." ;;
  esac
}
check_host() {                  # check host environment
  local Drive

  [ "${0:-}" = "${BASH_SOURCE:-}" ] && Runssourced="no" || Runssourced="yes"

  Hostsystem="$(grep '^ID=' /etc/os-release 2>/dev/null | cut -d= -f2 || echo 'unknown')"
  Hostarchitecture="$(uname -m)"
  case "$Hostarchitecture" in
    x86_64|x86-64|amd64|AMD64)                               Hostarchitecture="amd64 ($Hostarchitecture)" ;;
    aarch64|armv8|ARMv8|arm64v8)                             Hostarchitecture="arm64v8 ($Hostarchitecture)" ;;
    aarch32|armv8l|armv7|armv7l|ARMv7|arm32v7|armhf|armv7hl) Hostarchitecture="arm32v7 ($Hostarchitecture)" ;;
    arm32v6|ARMv6|armel)                                     Hostarchitecture="arm32v6 ($Hostarchitecture)" ;;
    arm32v5|ARMv5)                                           Hostarchitecture="arm32v5 ($Hostarchitecture)" ;;
    i686|i386|x86)                                           Hostarchitecture="i386 ($Hostarchitecture)" ;;
    ppc64*|POWER8)                                           Hostarchitecture="ppc64le ($Hostarchitecture)" ;;
    s390x)                                                   Hostarchitecture="s390x ($Hostarchitecture)" ;;
    mips|mipsel)                                             Hostarchitecture="mipsel ($Hostarchitecture)" ;;
    mips64*)                                                 Hostarchitecture="mips64el ($Hostarchitecture)" ;;
    *)                                                       Hostarchitecture="unknown ($Hostarchitecture)" ;;
  esac

  # Check libc from host. If same as in container, it is possible to share timezone file
  Hostlibc="unknown"
  ldd --version 2>&1 | grep -q    'musl libc'       && Hostlibc='musl'
  ldd --version 2>&1 | grep -q -E 'GLIBC|GNU libc'  && Hostlibc='glibc'

  # cgroup version
  case "$(stat -c"%T" -f /sys/fs/cgroup)" in
    tmpfs)     Cgroupversion="v1" ;;
    cgroup2fs) Cgroupversion="v2" ;;
    *)         Cgroupversion="UNKNOWN" ;;
  esac
  debugnote "Detected cgroup $Cgroupversion"
  
  # Check host time zone
  Hostlocaltimefile="$(myrealpath /etc/localtime)"      # Find time zone file in /usr/share/zoneinfo
  [ -e "$Hostlocaltimefile" ] || Hostlocaltimefile=""
  Hostutctime=$(date +%:::z)                            # Offset of UTC. Used if time zone file cannot be provided
  [ "$(cut -c1 <<< "$Hostutctime")" = "+" ] && {
    Hostutctime="UTC-$(cut -c2- <<< "$Hostutctime")"
  } || {
    Hostutctime="UTC+$(cut -c2- <<< "$Hostutctime")"
  }

  # Check for MS Windows subsystem
  command -v cygcheck.exe >/dev/null && {
    cygcheck.exe -V | rmcr | grep -q "(cygwin)"   && Winsubsystem="CYGWIN"
    cygcheck.exe -V | rmcr | grep -q "(msys)"     && Winsubsystem="MSYS2"
  }
  uname -r | grep -q "Microsoft"                  && Winsubsystem="WSL1"
  uname -r | grep -q "microsoft"                  && Winsubsystem="WSL2"
  case "$Winsubsystem" in
    MSYS2|CYGWIN)
      Winsubmount="$(cygpath.exe -u "c:/" | rmcr | sed s%/c/%%)"
      Winsubpath="$(convertpath unix "$(cygpath.exe -w "/" | rmcr)" )"
      Mobyvm="yes"
    ;;
    WSL1|WSL2)
      command -v "/mnt/c/Windows/System32/cmd.exe" >/dev/null && Winsubmount="/mnt"
      command -v "/c/Windows/System32/cmd.exe" >/dev/null     && Winsubmount=""
      grep -q "Windows" <<< "${PATH:-}" || export PATH="${PATH:-}:$Winsubmount/c/Windows/System32:$Winsubmount/c/Windows/System32/WindowsPowerShell/v1.0" # can miss after sudo in WSL
      command -v "$Winsubmount/c/Windows/System32/cmd.exe" >/dev/null || error "$Winsubsystem: Could not find cmd.exe
  in /mnt/c/Windows/System32 or /c/Windows/System32.
  Do you have a different path to your Windows system partition?"
      Winsubpath="$(convertpath unix "$(getwslpath)")"
      [ "$Winsubsystem" = "WSL1" ] && Mobyvm="yes"
    ;;
  esac
  Winsubmount="${Winsubmount%/}"
  Winsubpath="${Winsubpath%/}"
  [ "$Winsubsystem" ] && Hostsystem="MSWindows-$Winsubsystem"
  [ -z "$Mobyvm" ] && Mobyvm="no"

  # Check host IP. Needed for --pulseaudio=tcp, --printer=tcp, --xoverip and --xwin
  case "$Winsubsystem" in
    "")
      case "$Network" in
        host) Hostip="127.0.0.1" ;;
        *)
          #Hostip="$(hostname -I | cut -d' ' -f1)"
          [ "$Hostip" ] || Hostip="$(ip -4 -o a |                  awk '{print $4}' | cut -d/ -f1 | grep    "^192\.168\.*" | head -n1)"
          [ "$Hostip" ] || Hostip="$(ip -4 -o a | grep 'docker0' | awk '{print $4}' | cut -d/ -f1 | grep    "172.17.0.1" ||: )"
          [ "$Hostip" ] || Hostip="$(ip -4 -o a | grep 'docker0' | awk '{print $4}' | cut -d/ -f1 | head -n1)"
          [ "$Hostip" ] || Hostip="$(ip -4 -o a |                  awk '{print $4}' | cut -d/ -f1 | grep -v "127.0.0.1" | head -n1)"
        ;;
      esac
    ;;
    *)
                       Hostip="$(ipconfig.exe | rmcr | grep 'IPv4' | grep -o '192\.168\.[0-9]*\.[0-9]*'       | head -n1 )"
      [ "$Hostip" ] || Hostip="$(ipconfig.exe | rmcr | grep 'IPv4' | grep -o '10\.0\.[0-9]*\.[0-9]*'          | head -n1 )"
      [ "$Hostip" ] || Hostip="$(ipconfig.exe | rmcr | grep 'IPv4' | grep -o '[0-9]*\.[0-9]*\.[0-9]*\.[0-9]*' | head -n1 )"
    ;;
  esac

  # Provide dos->unix newline converter to unpriv() commands
  [ "$Winsubsystem" ] || rmcr() { cat; }
  export -f rmcr

  # Check whether x11docker runs over SSH
  pstree -ps $$ >/dev/null 2>&1 && {
    pstree -ps $$ | grep -q sshd && Runsoverssh="yes" || Runsoverssh="no"
  } || {
    check_parent_sshd "$$"       && Runsoverssh="yes" || Runsoverssh="no"
  }

  # Check whether x11docker runs on console
  Runsonconsole="$(env LANG=C tty 2>&1 ||:)"
  case "$Runsonconsole" in
    "not a tty") Runsonconsole="" ;;
    *)
      grep -q tty <<< "$Runsonconsole"        && Runsonconsole="yes" || Runsonconsole="no"
    ;;
  esac
  [ "$Winsubsystem"   ]                       && Runsonconsole="no"
  [ -z "$Runsonconsole" ] && {
    [ -n "${DISPLAY:-}${WAYLAND_DISPLAY:-}" ] && Runsonconsole="no"  || Runsonconsole="yes"
    debugnote "check_host(): Command tty failed. Guess if running on console: $Runsonconsole"
  }

  # Check whether x11docker runs in a terminal
  tty -s && Runsinterminal="yes" || Runsinterminal="no"

  # Check whether x11docker runs in interactive bash mode (--enforce-i)
  case $- in
    *i*) Runsinteractive="yes" ;;
    *)   Runsinteractive="no" ;;
  esac

  # Check whether ps can watch processes of other users
  mount | grep "^proc" | grep -q "hidepid=2" && {
    Hosthidepid="yes"
    debugnote "check_host(): /proc is mounted with hidepid=2."
  } || {
    Hosthidepid="no"
  }
  ps aux | cut -d' ' -f1 | grep -q root && {
    Hostcanwatchroot="yes"
  } || {
    Hostcanwatchroot="no"
    case "$Winsubsystem" in
      MSYS2|CYGWIN) Hostcanwatchroot="yes" ;;
    esac
  }
  debugnote "check_host(): ps can watch root processes: $Hostcanwatchroot"

  # Check if host uses proprietary NVIDIA driver
  Nvidiaversion=$(head -n1 2>/dev/null </proc/driver/nvidia/version | awk '{ print $8 }')
  #Nvidiaversion="470.74"

  # check python version
  Pythonbin="$(command -v python ||:)"
  [ -z "$Pythonbin" ] && Pythonbin="$(command -v python3 ||:)"
  [ -z "$Pythonbin" ] && Pythonbin="$(command -v python2 ||:)"
  "$Pythonbin" --version >/dev/null 2>&1 || Pythonbin=""
  case "$Backend" in
    docker|podman|nerdctl)
      [ -z "$Pythonbin" ] && error "x11docker needs 'python' to parse output
  of '$Backend inspect'. Please install 'python' version 2.x or 3.x."
    ;;
  esac

  return 0
}
check_hostuser() {              # check for unprivileged host user
  # check host user, want an unprivileged one to run X server
  # default behaviour:
  #  x11docker started as unprivileged user:          starting X server as this user and create same user in container
  #  x11docker started as root:                       determine real user with $(logname), instead of root use real user like above
  #  x11docker started as root with --hostuser=root:  root runs X server and root is container user (discouraged)
  #                                                   if you want root in container, just use --user=root
  #  x11docker with --user=someuser                   container user is someuser, host user is unprivileged user $(logname)
  #
  # root permissions are only needed to run docker. If started unprivileged, a password prompt appears.

  # user who started x11docker
  Startuser="$(id -un)"

  # not root? Use current user.
  [ -z "$Hostuser" ] && [ "$Startuser" != "root" ] && Hostuser="$Startuser"

  # root? find unprivileged user
  Lognameuser="$(logname 2>/dev/null ||:)"
  [ -z "$Lognameuser" ] && [ -z "$Hostuser" ] && note "Your terminal seems to be not POSIX compliant.
  Command 'logname' does not return a value.
  Consider to use another terminal emulator.
  Fallback: Will try to check \$SUDO_USER and \$PKEXEC_UID."
  [ -z "$Lognameuser" ] && [ -n "${SUDO_USER:-}" ]  && Lognameuser="${SUDO_USER:-}"  && [ -z "$Hostuser" ] && note "Will use \$SUDO_USER = ${SUDO_USER:-} as host user."
  [ -z "$Lognameuser" ] && [ -n "${PKEXEC_UID:-}" ] && Lognameuser="${PKEXEC_UID:-}" && [ -z "$Hostuser" ] && note "Will use user with uid \$PKEXEC_UID = ${PKEXEC_UID:-} as host user."
  [ -z "$Lognameuser" ] &&                             Lognameuser="$Startuser"      && [ -z "$Hostuser" ] && note "Will use \$(id -un) = $Lognameuser as host user."

  # option --hostuser
  [ -z "$Hostuser" ] && Hostuser="$Lognameuser"
  [ "$Hostuser" != "$Startuser" ] && {
    [ "$Startuser" = "root" ] || error "Option --hostuser: x11docker must run as root
   to choose a host user different from user '$Startuser'."
  }
  getent passwd "$Hostuser" >/dev/null 2>&1 || {
    [ -f /etc/passwd ] || warning "Your system misses /etc/passwd"
    error "Could not find user '$Hostuser' in /etc/passwd."
  }

  Hostuser="$(id -un "$Hostuser")"
  Hostuseruid="$(id -u "$Hostuser")"
  Hostusergid="$(id -g "$Hostuser")"
  [ "$Hostuser" = "$Startuser" ] && Hostuserhome="$HOME"

  [ -z "$Hostuserhome" ] && Hostuserhome=$(getent passwd "$Hostuser" 2>/dev/null | cut -d: -f6)
  [ -z "$Hostuserhome" ] && {
    Hostuserhome="/tmp/home/$Hostuser"
    mkdir -p "$Hostuserhome"
    warning "Could not read your home directory from /etc/passwd for user '$Hostuser'.
  Please set \$HOME with a valid path.
  Fallback: setting HOME=$Hostuserhome"
    check_fallback
  }
  debugnote "host user: $Hostuser $Hostuseruid:$Hostusergid $Hostuserhome"
  
  Localsharedir="$Hostuserhome/.local/share/x11docker"
  mkdir -p "$Localsharedir"
  Mitshmlib="$Localsharedir/XlibNoSHM.so"

  [ "$Hostuser" = "root" ] && warning "Running as user root.
  Maybe \$(logname) did not provide an unprivileged user.
  Please use option --hostuser=USER to specify an unprivileged user.
  Otherwise, new X server runs as root, and container user will be root."

  id | grep -q "(docker)" && warning "User $Hostuser is member of group docker.
  That allows unprivileged processes on host to gain root privileges."

  # How to run as unprivileged user in unpriv()
  case "$Hostuser" in
    "$Startuser") Unpriv="eval" ;;   # alternatively: bash -c
    *)            Unpriv="su $Hostuser -c" ;;
  esac

  return 0
}
check_hostxenv() {              # check environment variables for host X display
  Hostdisplay="${DISPLAY:-}"
  Hostdisplaynumber="$(echo "$Hostdisplay" | cut -d: -f2 | cut -d. -f1)"                       # display number without ":" and ".0"
  [ -n "$Hostdisplay" ] && Hostxsocket="/tmp/.X11-unix/X$Hostdisplaynumber" || Hostxsocket=""  # X socket from host, needed for --hostdisplay
  [ -e "$Hostxsocket" ] || Hostxsocket=""                                                      # can miss in SSH session

  # Check whether host X server has MIT-SHM enabled.
  command -v xdpyinfo >/dev/null && xdpyinfo >/dev/null 2>&1 && {
    xdpyinfo | grep -q "MIT-SHM" && Hostmitshm="yes" || Hostmitshm="no"
  }
  [ "$Winsubsystem" ] && Hostmitshm="no"

  # get cookie from host display
  XAUTHORITY=${XAUTHORITY:-}
  [ -z "$XAUTHORITY" ]   && command -v systemctl >/dev/null    && XAUTHORITY="$(systemctl --user show-environment | grep XAUTHORITY= | cut -d= -f2)"
  [ -z "$XAUTHORITY" ]   && [ -e "$Hostuserhome/.Xauthority" ] && XAUTHORITY="$Hostuserhome/.Xauthority"
  [ -z "$XAUTHORITY" ]   && [ "$Runsoverssh" = "yes" ] && [ -e "$Hostuserhome/.Xauthority" ] && XAUTHORITY="$Hostuserhome/.Xauthority"
  [ "${XAUTHORITY:-}" ]  && {
    unpriv "xauth -i -f '${XAUTHORITY:-}' nlist '$Hostdisplay' 2>/dev/null | sed -e 's/^..../ffff/' | xauth -f '$Hostxauthority' nmerge - 2>/dev/null"
    chown "$Hostuser" "$Hostxauthority"
    chmod 600 "$Hostxauthority"
    export XAUTHORITY
  } || {
    Hostxauthority=""
    unset XAUTHORITY
  }
  [ "$Hostdisplay" ] || {
    Hostxsocket=""
    Hostxauthority=""
    XAUTHORITY=""
    unset DISPLAY
    unset XAUTHORITY
  }
  [ -s "${XAUTHORITY:-}" ] && [ ! -s "$Hostxauthority" ] && cp "${XAUTHORITY:-}" "$Hostxauthority"

  # create $Hostxenv
  Hostxenv="DISPLAY=$Hostdisplay"
  [ -s "$Hostxauthority" ] && {
    Hostxenv="$Hostxenv XAUTHORITY=$Hostxauthority"
    export XAUTHORITY=$Hostxauthority
  } || {
    Hostxauthority=
    unset XAUTHORITY
  }
  [ -n "$Hostxsocket" ]       && Hostxenv="$Hostxenv XSOCKET=$Hostxsocket"
  [ -n "$Hostwaylandsocket" ] && Hostxenv="$Hostxenv WAYLAND_DISPLAY=$Hostwaylandsocket"
  Hostxenv="$Hostxenv XDG_RUNTIME_DIR=$XDG_RUNTIME_DIR"
  [ -n "$Hostdisplay" ] && [ -z "$Hostxauthority" ] && warning "Your host X server runs without cookie authentication."

  [ -z "$GDK_BACKEND" ] && {
    [ -n "$Hostwaylandsocket" ] && export GDK_BACKEND="wayland"
    [ -n "$Hostdisplay" ]       && export GDK_BACKEND="x11"
    [ -z "$Hostdisplay$Hostwaylandsocket" ] && unset GDK_BACKEND
  }

  return 0
}
check_snap() {                  # check if docker is installed in snap. Causes restrictions.
  # Check if docker is installed with snap/snappy (noteable Ubuntu Server)
  myrealpath "$(command -v "${Backendbin:-docker_not_found}")" | grep -q snap && Runsinsnap="yes" || Runsinsnap="no"
  
  [ "$Runsinsnap" = "yes" ] && [ -z "$Snapsupport" ] && {
    note "It seems docker runs in snap.
  This limits possibilities to use docker and x11docker.
  Fallback: Enabling option --snap"
    Snapsupport="yes"
    check_fallback
  }
  [ -d "/snap/docker" ] && [ "$Snapsupport" = "no" ] && note "Detected /snap/docker.
  If you run Docker in snap, you might need option --snap to support this setup."
  [ "$Snapsupport" = "yes" ] && {
    note "Option --snap to support a docker-in-snap setup
  causes some restrictions:
  Option --newprivileges=yes is enabled, snap needs it for unknown reasons.
  Option --xoverip is enabled because sharing unix sockets fails.
  Option --network is enabled because X over TCP is not possible otherwise.
  Option --hostdisplay is not available because it would need a shared unix socket.
  Option --xc to run X in container is not available.
  Option --gpu only works with --xorg and with indirect rendering (--gpu=iglx).
  Options --printer and --pulseaudio only work in TCP mode.
  Recommendation: Purge the Docker snap installation and install Docker natively."
    [ "$Allownewprivileges" = "auto" ] && Allownewprivileges="yes"
    Xoverip="yes"
    Xcontainer="no"
    Xtoolscontainer="no"
    [ "$Network" = "none" ] && Network=""
    case "$Sharegpu" in
      yes|virgl|iglx)
        Sharegpu="no"
        Virgl=""
        Iglx="yes"
      ;;
    esac
  }
  return 0
}
check_xdg_runtime_dir() {       # set up XDG_RUNTIME_DIR if needed
  [ -z "$XDG_RUNTIME_DIR" ] && [ -e "/run/user/${Hostuseruid:-unknownuid}" ] && export XDG_RUNTIME_DIR="/run/user/$Hostuseruid"
  case "$Xserver" in
    --weston|--kwin|--weston-xwayland|--kwin-xwayland)
      [ -z "$XDG_RUNTIME_DIR" ] && {
        export XDG_RUNTIME_DIR="$Cachefolder/XDG_RUNTIME_DIR"
        unpriv "mkdir -p  $XDG_RUNTIME_DIR"
        unpriv "chmod 700 $XDG_RUNTIME_DIR"
      }
    ;;
  esac
  return 0
}
create_cachefiles() {           # create empty cache files owned by unprivileged user
  local Line
  # create base cache folder
  [ "$Cachebasefolder" ] || {
    #Cachebasefolder="$Hostuserhome/.cache/x11docker"    ### FIXME really a good idea for MS Windows? WSL cache provides performance, but maybe must not be shared with container to avoid file access errors.
    case "$Winsubsystem" in
      ""|MSYS2|CYGWIN) Cachebasefolder="$Hostuserhome/.cache/x11docker" ;;
      WSL1|WSL2)
        case "$Mobyvm" in
          yes)
            Cachebasefolder="$(convertpath subsystem "$(wincmd "echo %userprofile%")")/x11docker/cache"
            mkdir -p "$Hostuserhome/.cache/x11docker/symlink"
            [ -e "$Hostuserhome/.cache/x11docker/symlink" ] || ln -s -T "$Cachebasefolder" "$Hostuserhome/.cache/x11docker/symlink"
            mkfile "$Hostuserhome/.cache/x11docker/symlink/symlink.txt"
            echo "x11docker: With MobyVM x11docker cache in WSL is stored in
$Cachebasefolder
to allow file sharing with containers.
A symbolic link is created in WSL at
$Hostuserhome/.cache/x11docker/symlink
" >> "$Hostuserhome/.cache/x11docker/symlink/symlink.txt"
          ;;
          no)
            Cachebasefolder="$Hostuserhome/.cache/x11docker"
          ;;
        esac
      ;;
    esac
  }
  [ "$Cachebasefolder" = "/x11docker/cache" ] && error "Failed to find a valid path for cache directory.
  Please report at https://github.com/mviereck/x11docker
  As a workaround you can specify a cache folder with --cachebasedir='DIR'"

  Cachebasefolder="$(convertpath subsystem "$Cachebasefolder")"
  [ "$Cachebasefolder" != "${Cachebasefolder//" "/""}" ] && error "Cache root folder must not contain whitespaces.
  $Cachebasefolder"
  unpriv "mkdir -p '$Cachebasefolder'"                   || error "Could not create cache folder
  $Cachebasefolder"
  writeaccess "$Hostuseruid" "$Cachebasefolder"          || error "User $Hostuser does not have write access to cache folder
  $Cachebasefolder"

  # Create cache subfolders
  Cachefolder="$Cachebasefolder/$Cachenumber-$(unspecialstring "$(basename "$Imagename")")"
  [ -d "$Cachefolder" ] && error "Cache folder already exists:
  $Cachefolder"

  [ "$Cachefolder" != "$(escapestring "$Cachefolder")" ] && error "Invalid name created for cache folder:
    $Cachefolder
  Most probably provided image name (or --exe command) is invalid in some way:
    $(escapestring "$Imagename")
  For special setups like command chains use a syntax like:
    x11docker IMAGENAME  --  sh -c \"cd /etc && xterm\""

  Sharefolder="$Cachefolder/$Sharefolder"
  unpriv "mkdir -p $Sharefolder"

  # Files in $Cachefolder: host only access
  Backendcommandfile="$Cachefolder/$Backendcommandfile"             && mkfile $Backendcommandfile
  Hostxauthority="$Cachefolder/$Hostxauthority"                     && mkfile $Hostxauthority
  Messagelogfile="$Cachefolder/$Messagelogfile"                     && mkfile $Messagelogfile
  Pulseaudioconf="$Cachefolder/$Pulseaudioconf"                     && mkfile $Pulseaudioconf
  Storepidfile="$Cachefolder/$Storepidfile"                         && mkfile $Storepidfile
  Watchpidfifo="$Cachefolder/$Watchpidfifo"
  Xservercookie="$Cachefolder/$Xservercookie"                       && mkfile $Xservercookie
  Xtermrc="$Cachefolder/$Xtermrc"                                   && mkfile $Xtermrc
                                                             unpriv "mkdir -p $Cachefolder/tmp/.X11-unix"
                                                         unpriv "mkdir -m 700 $Cachefolder/XDG_RUNTIME_DIR"

  # Files in $Sharefolder: shared to /x11docker in container
  Clipboardrc="$Sharefolder/$Clipboardrc"                           && mkfile $Clipboardrc
  Cmdrc="$Sharefolder/$Cmdrc"                                       && mkfile $Cmdrc
  Cmdstderrlogfile="$Sharefolder/$Cmdstderrlogfile"                 && mkfile $Cmdstderrlogfile 666
  Cmdstdinfifo="$Sharefolder/$Cmdstdinfifo"
  Cmdstdoutlogfile="$Sharefolder/$Cmdstdoutlogfile"                 && mkfile $Cmdstdoutlogfile 666
  Compositorlogfile="$Sharefolder/$Compositorlogfile"               && mkfile $Compositorlogfile
  Containerrc="$Sharefolder/$Containerrc"                           && mkfile $Containerrc
  Containerenvironmentfile="$Sharefolder/$Containerenvironmentfile" && mkfile $Containerenvironmentfile 666
  Containerlocaltimefile="$Sharefolder/$Containerlocaltimefile"
  Containerlogfile="$Sharefolder/$Containerlogfile"                 && mkfile $Containerlogfile 666
  Containerrootrc="$Sharefolder/$Containerrootrc"                   && mkfile $Containerrootrc
  Logfile="$Sharefolder/x11docker.log"                              && mkfile $Logfile 666
  Messagefifo="$Sharefolder/$Messagefifo"
  Nxagentclientrc="$Sharefolder/$Nxagentclientrc"                   && mkfile $Nxagentclientrc
  Nxagentkeysfile="$Sharefolder/$Nxagentkeysfile"                   && mkfile $Nxagentkeysfile
  Nxagentoptionsfile="$Sharefolder/$Nxagentoptionsfile"             && mkfile $Nxagentoptionsfile
  Pulseaudiocookie="$Sharefolder/$Pulseaudiocookie"
  Pulseaudiosocket="$Sharefolder/$Pulseaudiosocket"
  Storeinfofile="$Sharefolder/$Storeinfofile"                       && mkfile $Storeinfofile 666
  Systemdjournallogfile="$Sharefolder/$Systemdjournallogfile"       && mkfile $Systemdjournallogfile
  Timetosaygoodbyefile="$Sharefolder/$Timetosaygoodbyefile"         && mkfile $Timetosaygoodbyefile
  Timetosaygoodbyefifo="$Sharefolder/$Timetosaygoodbyefifo"
  Westonini="$Sharefolder/$Westonini"                               && mkfile $Westonini
  Xclientcookie="$Sharefolder/$Xclientcookie"                       && mkfile $Xclientcookie
  Xkbkeymapfile="$Sharefolder/$Xkbkeymapfile"                       && mkfile $Xkbkeymapfile
  Xinitlogfile="$Sharefolder/$Xinitlogfile"                         && mkfile $Xinitlogfile
  Xinitrc="$Sharefolder/$Xinitrc"                                   && mkfile $Xinitrc
  Xpraclientlogfile="$Sharefolder/$Xpraclientlogfile"               && mkfile $Xpraclientlogfile
  Xpraserverlogfile="$Sharefolder/$Xpraserverlogfile"               && mkfile $Xpraserverlogfile
  
  case "$Backend" in
    proot|chroot)
      ln -s "$Storepidfile" "$Sharefolder/store.pids"
    ;;
  esac

  # Files in $Cachebasefolder
  Logfilebackup="$Cachebasefolder/x11docker.log"
  Modelinefilebasepath="$Cachebasefolder/$Modelinefilebasepath"     && unpriv "mkdir -p $Modelinefilebasepath"

  # file to store display numbers in use today
  Numbersinusefile="$Cachebasefolder/$Numbersinusefile"
  for Line in $(find $Cachebasefolder/displaynumbers.* 2>/dev/null ||:) ; do
    [ "$Line" != "$Numbersinusefile" ] && rm "$Line"
  done
  [ -e "$Numbersinusefile" ] || mkfile "$Numbersinusefile"

  # libc timezone file
  [ -e "$Hostlocaltimefile" ] && cp "$Hostlocaltimefile" "$Containerlocaltimefile"

  storeinfo "cache=$Cachefolder"
  storeinfo "stdout=$Cmdstdoutlogfile"
  storeinfo "stderr=$Cmdstderrlogfile"

  return 0
}
setup_fifo() {                  # set up fifo channels (also option --stdin)
  # setup fifos to allow messages from within container and xinitrc
  # and to send pids to watch to watchpidlist() thread

  # file descriptors in use:
  # FDstderr            stderr                for warnings and notes redirected to &2, with --silent redirected to /dev/null
  # FDmessage           $Messagefifo          for messages from other threads to watchmessagefifo()
  # FDcmdstdin          stdin>>$Cmdstinfile   --stdin with catstdin, redirection of &0
  # FDtimetosaygoodbye  $Timetosaygoodbyefifo for saygoodbye() and waitfortheend()
  # FDwatchpid          $Watchpidfifo         for watchpidlist()
  case "$Mobyvm" in
    yes) Usemkfifo="no" ;;
    no)  Usemkfifo="yes" ;;
  esac

  [ "$Runtime" = "kata-runtime" ] && Usemkfifo="no"
  #[ "$Runtime" = "sysbox-runc" ]  && Usemkfifo="no"
  # redirect stdin to named pipe. Named pipe is shared with container and used as stdin of container command in containerrc
  [ "$Forwardstdin" = "yes" ] && {
    case "$Usemkfifo" in
      yes) unpriv "mkfifo '$Cmdstdinfifo'" ;;
      no)  mkfile "$Cmdstdinfifo" ;;
    esac
    #exec {FDcmdstdin}<>$Cmdstdinfifo
    #cat <&0 >>${FDcmdstdin} & storepid $! catstdin
    cat <&0 >>"$Cmdstdinfifo" & storepid $! catstdin
    storeinfo "stdin=$Cmdstdinfifo"
  }
  case "$Usemkfifo" in
    yes)
      unpriv "mkfifo '$Watchpidfifo'"
      unpriv "mkfifo '$Messagefifo' && chmod 666 '$Messagefifo'"
      unpriv "mkfifo '$Timetosaygoodbyefifo'"
    ;;
    no) # Windows, kata
      mkfile "$Watchpidfifo"
      mkfile "$Messagefifo" 666
      mkfile "$Timetosaygoodbyefifo" 666
    ;;
  esac

  # used by waitfortheend()
  exec {FDtimetosaygoodbye}<>"$Timetosaygoodbyefifo"

  # start watching important pids, e.g. xinit, container.
  exec {FDwatchpid}<>"$Watchpidfifo"
  watchpidlist & storepid $! watchpidlist

  # start watching for messages out of container
  exec {FDmessage}<>"$Messagefifo"
  watchmessagefifo & storepid $! watchmessagefifo

  return 0
}
setup_verbosity() {             # options --verbose, --stdout, --stderr
  local Line Logfiles
  # create summary logfile
  Logfiles="
    $Cmdstderrlogfile
    $Cmdstdoutlogfile
    $Compositorlogfile
    $Containerlogfile
    $Systemdjournallogfile
    $Messagelogfile
    $Xinitlogfile
    $Xpraclientlogfile
    $Xpraserverlogfile
    "

  [ -n "$Verbose" ] && [ "$Verbose" != "yes" ] && Logfiles="$(grep -E "$Verbose" <<< "$Logfiles")" # inofficial hack to optionally reduce log output
  
  for Line in $Logfiles; do
    [ -e "$Line" ] && grep -q "/" <<< "$Line" && Logfiles="$Logfiles $Line"
  done

  {
    trap '' SIGINT
    tail --pid="$$" --retry -n +1 -F $Logfiles 2>/dev/null >>"$Logfile" ||:
  } &

  # option --verbose
  [ "$Verbose" ] && {
    trap '' SIGINT
    case "$Verbosecolors" in
      no)  tail --pid="$$" --retry -n +1 -F "$Logfile" 2>/dev/null >&${FDstderr} ;;
      yes) tail --pid="$$" --retry -n +1 -F "$Logfile" 2>/dev/null | sed "
                                      /\(Failed to add fd to store\|Failed to set invocation ID\|Failed to reset devices.list\)/d;
                                      s/\(ERROR\|Error\|error\|FAILURE\|FATAL\|Fatal\|fatal\)/${Colredbg}\1${Colnorm}/g;
                                      s/\(Failed\|failed\|Failure\|failure\)/${Colred}\1${Colnorm}/g;
                                      s/\(WARNING\|Warning\|warning\)/${Colyellow}\1${Colnorm}/g;
                                      s/\(DEBUGNOTE\)/${Colblue}\1${Colnorm}/g;
                                      s/^==>.*/${Coluline}\0${Colnorm}/;
                                      s/\(Starting\|Activating\)/${Colgreen}\0${Colnorm}/;
                                      s/\(Started\|Reached target\|activated\)/${Colgreenbg}\0${Colnorm}/;
                                      s/^\(+\|++\|+++\)/${Colgreenbg}\0${Colnorm}/ ;
                                      s/^x11docker/${Colgreen}\0${Colnorm}/ " >&${FDstderr}
      ;;
    esac
  } &

  [ "$Showcontaineroutput" = "yes" ]    && {
    {
      waitforlogentry tailstdout "$Storeinfofile" "x11docker=ready" ""  infinity ||:
      trap '' SIGINT
      tail --pid="$$" -n +1 -f "$Cmdstdoutlogfile"     2>/dev/null ||:
    } &
    {
      waitforlogentry tailstderr "$Storeinfofile" "x11docker=ready" ""  infinity ||:
      trap '' SIGINT
      tail --pid="$$" -n +1 -f "$Cmdstderrlogfile" >&2 2>/dev/null ||:
    } &
  }

  return 0
}

#### options
parse_options() {               # parse cli options
  local Shortoptions Longoptions Parsedoptions Presetoptions Presetfile
  Shortoptions="aAcdDfFghHiIKlmnpPqtTvVwWxXyY"
  Shortoptions="${Shortoptions}e"                                                                                           # deprecated
  Longoptions="$Longoptions,auto,desktop,tty,wayland,wm::,xc::,xonly"                                                       # Influencing auto-setup of X/Wayland/x11docker
  Longoptions="$Longoptions,hostdisplay,nxagent,runx,xephyr,xpra,xpra2,xorg,xvfb,xwin"                                      # X servers
  Longoptions="$Longoptions,weston-xwayland,xpra-xwayland,xpra2-xwayland,xwayland"                                          # X servers depending on a Wayland compositor
  Longoptions="$Longoptions,hostwayland,kwin,weston"                                                                        # Wayland compositors without X
  Longoptions="$Longoptions,border::,dpi:,fullscreen,output-count:,rotate:,scale:,size:,xfishtank"                          # X/Wayland appearance options
  Longoptions="$Longoptions,clean-xhost,composite::,display:,keymap:,no-auth,vt::,westonini:,xhost::,xoverip,xtest::"       # X/Wayland config
  Longoptions="$Longoptions,enforce-i,fallback::,preset:,pull::"                                                            # x11docker config
  Longoptions="$Longoptions,cachebasedir:,home::,homebasedir:,share:"                                                       # Host folders
  Longoptions="$Longoptions,alsa::,clipboard,gpu::,lang::,printer::,pulseaudio::,webcam"                                    # Host integration features
  Longoptions="$Longoptions,backend:,env:,mobyvm,name:,no-entrypoint,no-setup,runtime:,snap,workdir:"                       # Container config
  Longoptions="$Longoptions,cap-default,ipc::,limit::,newprivileges::,network::"                                            # Container capabilities
  Longoptions="$Longoptions,group-add:,hostuser:,password::,sudouser::,user:,shell:"                                        # Container user
  Longoptions="$Longoptions,dbus::,init::,hostdbus,sharecgroup"                                                             # Container init and DBus
  Longoptions="$Longoptions,stdin,interactive"                                                                              # Container interaction
  Longoptions="$Longoptions,runasuser:,runfromhost:,runasroot:"                                                             # Additional commands to execute
  Longoptions="$Longoptions,printenv::,printid::,printinfofile::,printpid1::"                                               # Output of vars on stdout
  Longoptions="$Longoptions,debug,quiet,verbose::"                                                                          # Verbose options
  Longoptions="$Longoptions,build,cleanup,help,launcher,licence,license,version"                                            # Special options without starting X or container
  Longoptions="$Longoptions,install,remove,update,update-master"                                                            # Installation
  #
  Longoptions="$Longoptions,keepcache,remountcgroup,xopt:,xorgconf:"                                                        # Experimental
  Longoptions="$Longoptions,dbus-system,exe,homedir:,hostipc,hostnet,iglx,kwin-xwayland,no-internet,no-xhost"               # Deprecated
  Longoptions="$Longoptions,sharedir:,sharessh,systemd,showenv,showid,showinfofile,showpid1"                                # Deprecated
  Longoptions="$Longoptions,cachedir:,no-init,nothing,no-xtest,openrc,podman,pull::,ps,pw::,runit,silent,starter"           # Removed
  Longoptions="$Longoptions,stderr,stdout,sys-admin,sysvinit,tini,trusted,untrusted,vcxsrv,xdummy"                          # Removed

  # default preset files
  parse_preset "default" ||:

  # options from cli
  Parsedoptions="$(getopt --options "$Shortoptions" --longoptions "$Longoptions" --name "$0" -- "$@" )" || error "Failed to parse options."
  eval set -- "$Parsedoptions"
  Parsedoptions_global="$Parsedoptions_global
  () $Parsedoptions"

  [ "$*" = "-h --" ] &&       usage &&        exit 0                 # Catch single -h for usage info, otherwise it means --hostdisplay
  [ "$*" = "--" ]    &&       usage &&        exit 0                 # x11docker without options

  while [ $# -gt 0 ]; do
    Optionset="$Optionset
${1:-}"
    case "${1:-}" in
         --help)              usage ;         exit 0 ;;              # Show help/usage and exit
         --license|--licence) license ;       exit 0 ;;              # Show MIT license and exit
         --version)           echo $Version ; exit 0 ;;              # Output version number and exit
         --xonly)             Backend="host"                         # Only create X server
                              Containercommand="sleep infinity"
                              Showdisplayenvironment="${Showdisplayenvironment:-yes}" ;;
         --preset)                                                   # Predefined option sets
                              parse_preset "${2:-}" || error "Option --preset: File not found: ${2:-}
  Searching in:
  $Presetdirlocal
  $Presetdirsystem"
                              shift ;;

     #### Choice of X servers and Wayland compositors
         --auto)              Autochooseserver="yes" ;;              # Default: auto-choose X server or Wayland compositor
      -h|--hostdisplay)       Xserver="--hostdisplay" ;;             # Host display :0 with shared X socket
      -H|--hostwayland)       Xserver="--hostwayland" ;;             # Host wayland. Allows coexistence with option
      -K|--kwin)              Xserver="--kwin" ;;                    # KWin, Wayland only
      -n|--nxagent)           Xserver="--nxagent" ;;                 # nxagent
         --runx)              Xserver="--runx" ;;                    # MS Windows: Will be Xwin or VcXsrv
      -t|--tty)               Xserver="--tty"  ;;                    # Do not provide any X nor Wayland
      -T|--weston)            Xserver="--weston" ;;                  # Weston, Wayland only
      -Y|--weston-xwayland)   Xserver="--weston-xwayland" ;;         # Weston + Xwayland
      -y|--xephyr)            Xserver="--xephyr" ;;                  # Xephyr
      -a|--xpra)              Xserver="--xpra" ;;                    # xpra
         --xpra2)             Xserver="--xpra2" ;;                   # xpra server in container and client on host
      -A|--xpra-xwayland)     Xserver="--xpra-xwayland" ;;           # Xpra with vfb Xwayland
         --xpra2-xwayland)    Xserver="--xpra2-xwayland" ;;          # Xpra with vfb Xwayland
      -x|--xorg)              Xserver="--xorg" ;;                    # Xorg
         --xvfb)              Xserver="--xvfb" ;;                    # Xvfb. Invisible on host.
      -X|--xwayland)          Xserver="--xwayland" ;;                # Xwayland on already running Wayland
         --xwin)              Xserver="--xwin" ;;                    # XWin, MS Windows only

     #### Influencing automatic choice of X server or Wayland compositor
      -d|--desktop)           Desktopmode="yes" ;;                   # image contains a desktop environment.
      -g)                     Sharegpu="yes" ;;                      # GPU access
         --gpu)               Sharegpu="${2:-yes}" ; shift ;;        # GPU access
      -W|--wayland)           Setupwayland="yes" ;;                  # set up wayland environment, regards --desktop
      -w)                                      Windowmanagermode="auto" ; Desktopmode="yes" ;;
         --wm)                case "${2:-}" in                       # choose window manager
                                "n"|"none")    Windowmanagermode="none" ;;
                                "host")        Windowmanagermode="host" ;;
                                ""|"auto"|"m") Windowmanagermode="auto" ;;
                                *)             Windowmanagermode="auto"; Windowmanagercommand="${2:-}" ;;
                              esac
                              shift ; Desktopmode="yes" ;;
         --xc)                Xcontainer="${2:-yes}" ; shift ;;      # Run X server in container

     #### X and Wayland appearance
         --border)            Xpraborder="${2:-"blue,1"}"; shift ;;  # Colored border for xpra clients
         --dpi)               Dpi=${2:-} ; shift ;;                  # Dots per inch. Influences font size
      -f|--fullscreen)        Fullscreen="yes"  ;;                   # Fullscreen mode for Xephyr and Weston
         --output-count)      Outputcount="${2:-}" ; shift ;;        # Number of virtual outputs
         --rotate)            Rotation=${2:-} ; shift ;;             # Rotation and mirroring
         --scale)             Scaling=${2:-} ; shift ;;              # Zoom
         --size)              Screensize="${2:-}" ;  shift ;;        # Screen size
      -F|--xfishtank)         Xfishtank="yes" ;;                     # Run xfishtank on new X server

     #### X and Wayland configuration
         --composite)         Xcomposite="${2:-yes}" ; shift ;;      # Enable or disable X extension COMPOSITE
         --display)           Newdisplaynumber="${2:-}"              # Display number to use for new X server or Wayland compositor
                              [ "$(cut -c1 <<< "$Newdisplaynumber")" = ":" ] && Newdisplaynumber="$(cut -c2- <<< "$Newdisplaynumber")"
                              shift ;;
         --keymap)            Xkblayout="${2:-}" ; shift ;;          # Keymap layout for xkbcomp. Compare /usr/share/X11/xkb/symbols
         --vt)                Newxvt="${2:-auto}" ; shift ;;         # Virtual console to use for --xorg
         --xoverip)           Xoverip="yes" ;;                       # Use X over TCP/IP instead of sharing X socket
         --xtest)             Xtest="${2:-yes}" ; shift ;;           # X extension XTEST
         --westonini)         Customwestonini="${2:-}" ; shift ;;    # Custom weston.ini

     #### X Authentication
         --clean-xhost|--no-xhost) Cleanxhost="yes"                  # Disable xhost credentials on host X
                              [ "${1:-}" = "--no-xhost" ] && note "Option --no-xhost is deprecated.
  Please use --clean-xhost instead." ;;
         --no-auth)           Xauthentication="no" ;;                # Disable cookie authentication on new X, set xhost +. Use for debugging only
         --xhost)             Xhost="${2:-auto}" ; shift ;;          # Custom xhost setting on new X server

     #### Host integration options
         --alsa)              Sharealsa="yes"                        # ALSA sound (shares /dev/snd)
                              Alsacard="${2:-$Alsacard}" ; shift ;;
      -c|--clipboard)         Shareclipboard="yes"  ;;               # Clipboard sharing
      -l)                     Langwunsch="$Langwunsch
${LANG:-}"                                                           # Locale/language setting
                              Langwunsch="${Langwunsch:-$LC_ALL}"
                              [ "$Langwunsch" ] || note "Option --lang: Environment variable \$LANG is empty.
  Please specify desired language locale with e.g. --lang=en_US or --lang=zh_CN." ;;
         --lang)              Langwunsch="$Langwunsch
${2:-${LANG:-}}" ; shift                                             # Locale/language setting
                              Langwunsch="${Langwunsch:-$LC_ALL}"
                              [ "$Langwunsch" ] || note "Option --lang: Environment variable \$LANG is empty.
  Please specify desired language locale with e.g. --lang=en_US or --lang=zh_CN." ;;
      -P|--printer)           Sharecupsmode="${2:-auto}" ; shift ;;   # Printer sharing with CUPS
      -p)                     Pulseaudiomode="auto" ;;               # Pulseaudio sound
         --pulseaudio)        Pulseaudiomode="${2:-auto}"; shift ;;  # Pulseaudio sound
         --webcam)            Sharewebcam="yes" ;;                   # Webcam sharing

     #### Special options
         --enforce-i)         ;;                                     # Run in bash interactive mode. Parsed at begin of script, nothing to do here.
         --fallback)          Fallback="${2:-yes}" ; shift ;;        # Allow/deny fallbacks for impossible options
      -i|--interactive)       Interactive="yes" ;;                   # Interactive terminal
         --runasroot)         Runasroot="$Runasroot
${2:-}"                       ; shift ;;                             # Add custom root command in container setup script
         --runasuser)         Runasuser="$Runasuser
${2:-}"
                              shift ;;                               # Add custom user command in cmdrc
         --runfromhost)       Runfromhost="$Runfromhost
${2:-}"                       ; shift ;;                             # Add custom host command in xinitrc

     #### User settings
         --group-add)         Containerusergroups="$Containerusergroups ${2:-}" ; shift ;; # Additional groups for container user
         --hostuser)          Hostuser="${2:-}" ; shift ;;           # Set host user different from logged in user
         --password)          Containeruserpassword="${2:-INTERACTIVE}" ; shift ;; # Change encrypted password in ~/.config/x11docker/passwd
         --shell)             Containerusershell="${2:-}" ; shift ;; # Set preferred user shell
         --sudouser)          Sudouser="${2:-yes}" ; shift ;;        # su and sudo for container user with password x11docker
         --user)              Containeruser="${2:-}"  ; shift ;;     # Set container user other than host user

     #### Init system and DBus
         --dbus)              Dbusrunsession="${2:-yes}" ; shift ;;  # DBus in container, Default: user session, =system: with system daemon
         --hostdbus)          Sharehostdbus="yes" ;;                 # Connect to host DBus
         --init)              Initsystem="${2:-tini}" ; shift ;;     # init in container
         --sharecgroup)       Sharecgroup="yes" ;;                   # Share /sys/fs/cgroup. Default for --init=systemd, possible use with --init=openrc or elogind.
         --systemd)           Initsystem="systemd" ; note "Option --systemd is deprecated. Please use: --init=systemd" ;;

     #### Container configuration
         --backend)           Backend="${2:-}" ; shift ;;   # container backend to use: docker, podman, nerdctl, others
         --cap-default)       Capdropall="no" ;;                     # Don't use --cap-drop=ALL --security-opt=no-new-privileges
         --env)               store_runoption env "${2:-}"           # Set container environment variables
                              shift ;;
         --ipc)               Shareipc="${2:-host}" ; shift ;;
         --limit)             Limitresources="${2:-0.5}" ; shift ;;  # Limited CPU and RAM access
         --mobyvm)            Mobyvm="yes" ;;                        # Use MobyVM in WSL2
         --name)              Containername="${2:-}" ; shift ;;      # Set container name
      -I)                     Network="" ;;
         --network)           Network="${2:-}" ; shift ;;            # Enable internet access
         --newprivileges)     Allownewprivileges="${2:-yes}" ; shift ;; # [Don't] set --security-opt=no-new-privileges
         --no-entrypoint)     Noentrypoint="yes" ;;                  # Disable ENTRYPOINT of image
         --no-setup)          Containersetup="no" ;;                 # No setup of x11docker inside of container (noteable disables containerrootrc() )
         --runtime)           Runtime="${2:-}" ; shift               # Runtime=runc|nvidia|kata-runtime|crun
                              [ "$Runtime" = "kata" ]   && Runtime="kata-runtime"
                              [ "$Runtime" = "sysbox" ] && Runtime="sysbox-runc" ;;
         --snap)              Snapsupport="yes" ;;                   # snap fallback mode
         --stdin)             Forwardstdin="yes" ;;                  # Forward stdin to container command
         --workdir)           Workdir="${2:-}" ; shift ;;            # Set working directory

     #### host folders and docker volumes
      -m)                     Sharehome="host" ;;
         --home|--homedir)    Sharehome="yes"                        # Share host folder as HOME in container, ~/x11docker/imagename or $2
                              [ "${1:-}" = "--homedir" ] && note "Option --homedir is deprecated.
  Please use --home=DIR instead."
                              Persistanthomevolume="${2:-}" ; shift ;;
         --share|--sharedir)  store_runoption volume "${2:-}"         # Share host file, device or directory
                              [ "${1:-}" = "--sharedir" ] && note "Option --sharedir is deprecated.
  Please use option --share=PATH instead."
                              shift ;;
         --homebasedir)       Hosthomebasefolder="${2:-}" ; shift ;; # Set base folder for --home instead of ~/.local/share/x11docker
         --cachebasedir)      Cachebasefolder="${2:-}" ; shift ;;    # Set base folder for cache  instead of ~/.cache/x11docker

     #### Verbosity options
      -D|--debug)             Debugmode="yes" ;;                     # Debugging mode
      -v|--verbose)           Verbose="${2:-yes}" ; shift ;;                 # Be verbose
      -V)                     Verbose="${Verbose:-yes}"; Verbosecolors="yes";;   # Be verbose with colored output
      -q|--quiet)             Silent="yes" ;;                        # Do not show warnings or errors
         --printenv)          Showdisplayenvironment="${2:-yes}" ; shift ;;  # Output of display number and cookie file on stdout. Catch with: read xenv < <(x11docker --printenv)
         --printid)           Showcontainerid="${2:-yes}" ; shift ;;         # Output of container id on stdout
         --printinfofile)     Showinfofile="${2:-yes}" ; shift ;;            # Show path to $Storeinfofile
         --printpid1)         Showcontainerpid1pid="${2:-yes}" ; shift ;;    # Output of host PID of container PID 1

     #### Special options not starting X or docker
         --build)             Buildimage="yes" ;;                    # Build an image from x11docker repository
         --cleanup)           Cleanup="yes"  ;;                      # Remove orphaned containers and cache files
         --install|--update|--update-master|--remove) Installermode="${1:-}" ;;   # Installer
         --launcher)          Createlauncher="yes" ;;                # Create application launcher on desktop and exit

     #### Experimental options
         --keepcache)         Preservecachefiles="yes" ;          note "Option --keepcache: experimental option." ;;
         --remountcgroup)     Remountcgroup="yes" ;               note "Option --remountcgroup: experimental option." ;;
         --xopt)              Xserveroptions="${2:-}" ;   shift ; note "Option --xopt: experimental option." ;;      # Custom X server options
         --xorgconf)          Xorgconf="${2:-}" ;         shift ; note "Option --xorgconf: experimental option." ;;  # Custom xorg.conf

     #### Deprecated options
         --dbus-system)       note "Option --dbus-system is deprecated.
  Please use one of --init=systemd|openrc|runit|sysvinit instead.
  Possible but discouraged is --dbus=system.
  Fallback: Enabling options --dbus=system --cap-default"
                              check_fallback
                              Dbusrunsession="system"
                              Capdropall="no" ;;
      -e|--exe)               Backend="host"
                              note "Option -e, --exe is deprecated.
  Please use --backend=host instead." ;;
         --hostipc)           Shareipc="host"
                              note "Option --hostipc is deprecated.
  Please use --ipc=host instead." ;;
         --hostnet)           Network="host"
                              note "Option --hostnet is deprecated.
  Please use --network=host instead." ;;
         --iglx)              Sharegpu="iglx"           ;    note "Option --iglx is deprecated.
  Please use option --gpu=iglx instead." ;;
         --kwin-xwayland)     Xserver="--kwin-xwayland" ;    note "Option --kwin-xwayland is deprecated.
  Please use option --weston-xwayland instead." ;;
         --no-internet)       Network="none"
                              note "Option --no-internet is deprecated.
  Option --network=none is default now." ;;
         --sharessh)          [ -e "${SSH_AUTH_SOCK:-}" ] && {       # SSH socket sharing
                                store_runoption volume "$(dirname "$SSH_AUTH_SOCK")"
                                store_runoption env "SSH_AUTH_SOCK=$(escapestring "${SSH_AUTH_SOCK:-}")"
                              } || note "Option --sharessh: environment variable \$SSH_AUTH_SOCK not set:" ;
                              note "Option --sharessh is deprecated.
  Please use (directly or with help of option --preset):
  --share \$(dirname \$SSH_AUTH_SOCK) --env SSH_AUTH_SOCK=\"\$SSH_AUTH_SOCK\"" ;;
         --showenv)           Showdisplayenvironment="yes" ; note "Option --showenv is deprecated.
  Please use option --printenv instead." ;;
         --showid)            Showcontainerid="yes" ;        note "Option --showid is deprecated.
  Please use option --printid instead." ;;
         --showinfofile)      Showinfofile="yes" ;           note "Option --showinfofile is deprecated.
  Please use option --printinfofile instead." ;;
         --showpid1)          Showcontainerpid1pid="yes" ;   note "Option --showpid1 is deprecated.
  Please use option --printpid1 instead." ;;

     #### Removed options
         --vcxsrv)            error "Option --vcxsrv is no longer supported.
  Please use either option --xwin in Cygwin/X
  or use option --runx in WSL or MSYS2.
  For 'runx' look at:  https://github.com/mviereck/runx" ;;
         --no-init|--openrc|--runit|--sysvinit|--tini)
                              error "Option ${1:-} has been removed.
  Please use option --init=INITSYSTEM instead." ;;
         --cachedir|--nothing|--no-xtest|--podman|--ps|--pull|--pw|--silent|--starter|--stderr|--stdout|--sys-admin|--trusted|--untrusted)
                              error "Option ${1:-} has been removed.
  Please have a look at 'x11docker --help' for possible replacements
  or search for '${1:-}' in /usr/share/doc/x11docker/CHANGELOG.md." ;;

     ##### Custom docker options / image name + container command. Everything after --
      --)
        shift
        [ "$(cut -c1 <<< "${1:-}")"  = "-" ] && grep -q " -- "  <<< " $* " && {
          while [ $# -gt 0 ] ; do
            [ "${1:-}" = "--" ] && shift && break
            Customdockeroptions="$Customdockeroptions '${1:-}'"
            shift
          done
        }
        while [ $# -gt 0 ] ; do
          [ -n "${1:-}" ] && [ -z "$Imagename" ] && [ "$(echo "${1:-}" | cut -c1)"  = "-" ]  && Customdockeroptions="$Customdockeroptions ${1:-}"
          [ -n "${1:-}" ] && [ -z "$Imagename" ] && [ "$(echo "${1:-}" | cut -c1)" != "-" ]  && Imagename="${1:-}" && shift
          [ -n "${1:-}" ] && [ -n "$Imagename" ] && Containercommand="$Containercommand '${1:-}'"
          shift
        done
      ;;
      '') ;;
      *) error "Unknown option ${1:-}
  Parsed options:
  $Parsedoptions" ;;
    esac
    shift
  done

  # Generate a list of options in use
  for Line in -a,--xpra -A,--xpra-xwayland -c,--clipboard -d,--desktop -D,--debug -f,--fullscreen -g,--gpu -e,--exe -F,--xfishtank -h,--hostdisplay -H,--hostwayland -i,--interactive -I,--network -K,--kwin -l,--lang -m,--home -n,--nxagent -p,--pulseaudio -P,--printer -q,--quiet -t,--tty -T,--weston -v,--verbose -w,--wm -W,--wayland -x,--xorg -X,--xwayland -y,--xephyr -Y,--weston-xwayland; do
    grep -q -x -- "$(cut -d, -f1 <<< "$Line")" <<< "$Optionset" && Optionset="$Optionset
$(cut -d, -f2 <<< "$Line")"
  done
  Optionset="$(grep -- '--' <<< "$Optionset" | sort | uniq)"
  Optionset="$(grep -v -x -- '--' <<< "$Optionset")"
  for Line in $(tr "," "\n" <<< "$Longoptions"); do
    Optionsetall="$Optionsetall
$(sed 's/^/--/g ; s/://g' <<< "$Line")"
  done
  Optionsetall="$(grep . <<< "$Optionsetall")"

  return 0
}
parse_preset() {                # nested parsing for --preset
  local Presetfile Presetoptions

  # file already parsed? Avoid a loop
  grep -q -x "${1:-}" <<< "$Presetlist" && return 0
  Presetlist="$Presetlist
${1:-}"

  # check global and local preset dir. Prefer local one.
  [ -f "$Presetdirsystem/${1:-}" ] && Presetfile="$Presetdirsystem/${1:-}"
  [ -f "$Presetdirlocal/${1:-}" ]  && Presetfile="$Presetdirlocal/${1:-}"
  [ -f "$Presetfile" ] || return 1

  # parse
  Presetoptions="$(sed '/^#/d' < "$Presetfile" | tr '\n' ' ')"
  [ -n "$(tr -d ' ' <<< "$Presetoptions")" ] && {
    note "Option --preset: Parsing $Presetfile:
  $Presetoptions"
    eval parse_options "$Presetoptions" # eval to preserve whitespace in arguments
  }
}
check_options_arguments() {     # check for [likely] valid arguments
  # not checked here yet:
  # --home
  # --cachebasedir
  # --alsa
  # --network
  # --lang
  # --ipc
  # --border
  # --wm
  # --xhost
  # --group-add
  ## --hostuser
  ## --password
  ## --user
  # --env
  ## --shell
  ## --workdir
  # --print*
  # --preset
  ## --runasroot
  ## --runfromhost
  ## --runasuser
  # --runtime

  # --fallback
  case "$Fallback" in
    yes|no) ;;
    *) error "Option --fallback: Unknown argument '$Fallback'" ;;
  esac

  # CUSTOM_RUN_OPTIONS
  Customdockeroptions="${Customdockeroptions//"--cap-add "/"--cap-add="}"
  Customdockeroptions="${Customdockeroptions//"--runtime "/"--runtime="}"
  Customdockeroptions="${Customdockeroptions//"--ipc "/"--ipc="}"
  Customdockeroptions="${Customdockeroptions//"--network "/"--network="}"
  Customdockeroptions="${Customdockeroptions//"--net "/"--network="}"
  Customdockeroptions="${Customdockeroptions//"--net="/"--network="}"
  Customdockeroptions="${Customdockeroptions//"--user "/"--user="}"
  grep -q -- "--runtime=kata-runtime"   <<< "$Customdockeroptions" && Runtime="kata-runtime"
  grep -q -- "--runtime=nvidia"         <<< "$Customdockeroptions" && Runtime="nvidia"
  grep -q -- "--runtime=runc"           <<< "$Customdockeroptions" && Runtime="runc"
  grep -q -- "--runtime=crun"           <<< "$Customdockeroptions" && Runtime="crun"
  grep -q -- "--runtime=sysbox-runc"    <<< "$Customdockeroptions" && Runtime="sysbox-runc"

  # --backend
  [ -z "$Imagename" ] && {
    # Only create X server / --xonly
    Backend="${Backend:-host}"
    Containercommand="sleep infinity"
    Showdisplayenvironment="${Showdisplayenvironment:-yes}"
  }
  Backend="${Backend:-docker}"
  case "$Backend" in
    docker|podman|nerdctl|proot|host) ;;
    chroot|*) 
      error "Option --backend: Unknown argument '$Backend'."
    ;;
  esac

  # --dbus [=system]
  case "$Dbusrunsession" in
    yes|user|session) Dbusrunsession="yes" ;;
    no) ;;
    system)
      Dbusrunsession="yes"
      Dbussystem="yes"
    ;;
    *)
      note "Option --dbus: Unknown argument '$Dbusrunsession'.
  Fallback: Enabling --dbus user session."
      check_fallback
      Dbusrunsession="yes"
    ;;
  esac
  
  # --dpi
  [ "$Dpi" ] && {
    isnum "$Dpi" || {
      note "Option --dpi only accepts numeric values. Not numeric: '$Dpi'
  Fallback: Disaling option --dpi."
      check_fallback
      Dpi=""
    }
  }

  # --gpu
  case "$Sharegpu" in
    yes|no) ;;
    iglx)
      Iglx="yes"
      Sharegpu="no"
    ;;
    virgl)
      Virgl="yes"
      Sharegpu="no"
      [ "$Xcontainer" = "auto" ] && Xcontainer="yes"
    ;;
    *)
      note "Option --gpu: Unknown argument '$Sharegpu'.
  Fallback: Disabling option --gpu."
      check_fallback
      Sharegpu="no"
    ;;
  esac

  # --homebasedir
  [ "$Hosthomebasefolder" ] && {
    Hosthomebasefolder="$(convertpath subsystem "$Hosthomebasefolder")"
    [ -e "$Hosthomebasefolder" ] || error "Option --homebasedir: Specified path does not exist:
  $Hosthomebasefolder"
  }

  # --init
  case "$Initsystem" in
    tini|systemd|sysvinit|openrc|runit|dockerinit|s6-overlay|none) ;;
    *)
      note "Option --init: Unknown init system $Initsystem
  Possible: tini systemd sysvinit openrc runit s6-overlay none
  Fallback: Using --init=tini instead."
      check_fallback
      Initsystem="tini"
    ;;
  esac

  # --limit N
  [ "$Limitresources" ] && {
    [ "1" = "$(awk -v a="$Limitresources" "BEGIN {print (a <= 1)}")" ] && [ "1" = "$(awk -v a="$Limitresources" "BEGIN {print (a > 0)}")" ] || {
      warning "Option --limit: Specified value '$Limitresources' is out of range.
  Allowed is a factor greater than 0 and less than or equal to 1.  0<FACTOR<=1
  Fallback: Setting limit factor to --limit=0.5"
      check_fallback
      Limitresources="0.5"
    }
  }

  # --name
  [ -n "$Containername" ] && {
    [ "$Containername" = "$(unspecialstring "$Containername")" ] || error "Option --name: container name must not contain special chars.
  Compare:
  $Containername
  $(unspecialstring "$Containername")"
  }

  # --newprivileges
  case "$Allownewprivileges"
  in
    yes|no|auto) ;;
    *)
      note "Option --newprivileges: Unknown argument '$Allownewprivileges'.
  Fallback: Setting --newprivileges=auto"
      check_fallback
      Allownewprivileges="auto"
    ;;
  esac

  # --output-count
  [[ "$Outputcount" =~ ^[1-9]$ ]] || {
    note "Option --output-count: Value must be one of 1 2 3 4 5 6 7 8 9
  Disabling invalid value $Outputcount"
    check_fallback
    Outputcount="1"
  }

  # --printer
  case "$Sharecupsmode" in
    socket|tcp|auto|"") ;;
    *)
      note "Option --printer: Unknown argument '$Sharecupsmode'.
  Fallback: Setting --printer=auto."
      check_fallback
      Sharecupsmode="auto"
    ;;
  esac

  # --pulseaudio
  case "$Pulseaudiomode" in
    socket|tcp|auto|host|"") ;;
    *)
      note "Option --pulseaudio: Unknown argument '$Pulseaudiomode'.
  Fallback: Setting --pulseaudio=auto."
      check_fallback
      Pulseaudiomode="auto"
    ;;
  esac

  # --rotate
  [ -n "$Rotation" ] && {
    grep -q -w "$Rotation" <<< "0 90 180 270 flipped flipped-90 flipped-180 flipped-270" || {
      note "Option --rotate: Unsupported value '$Rotation'.
  Must be one of 0 90 180 270 flipped flipped-90 flipped-180 flipped-270
  Fallback: disabling option --rotate"
      check_fallback
      Rotation=""
    }
  }

  # --scale
  [ -n "$Scaling" ] && {
    isnum "$Scaling" || {
      note "Option --scale needs a number. '$Scaling' is not allowed.
  Fallback: disabling option --scale"
      check_fallback
      Scaling=""
    }
  }

  # --share
  while read Line; do
    grep -q -E "/|\\|:" <<< "$Line" && Line="$(convertpath subsystem "$Line")"
    [ "${Line:0:1}" != "/" ] && grep -q "/" <<< "$Line" && error "Option --share needs either an absolute path
  or a volume name without any '/':
  $Line"
    [ "${Line:0:1}" = "/" ] && [ ! -e "$Line" ] && error "Option --share: File or folder does not exist:
  $Line"
    [ -z "$Line" ] && error "Option --share needs an argument"
  done < <(store_runoption dump volume)

  # --size
  [ "$Screensize" ] && {
    isint "$(cut -d"x" -f1 <<< "$Screensize")" && isint "$(cut -d"x" -f2 <<< "$Screensize")" && grep -q "x" <<< "$Screensize" || {
      note "Option --size: Invalid argument: $Screensize
  Must consist of two integers with an x between them.
  Fallback: Disabling option --size"
      check_fallback
      Screensize=""
    }
  }

  # --sudouser
  Sudouser="${Sudouser,,}"
  case "$Sudouser" in
    no|"") Sudouser="" ;;
    yes|nopasswd) ;;
    *)
      note "Option --sudouser: Unknown argument '$Sudouser'.
  Fallback: Disabling option --sudouser."
      Sudouser=""
      check_fallback
    ;;
  esac

  # --vt
  [ -n "$Newxvt" ] && {
    isint "$Newxvt" || note "Option --vt: Argument must be a positive integer: $Newxvt
  Fallback: Disabling option --vt"
    check_fallback
    Newxvt=""
  }

  # --xtest
  case "$Xtest" in
    yes|no|"") ;;
    *)
      note "Option --xtest: Unknown argument: $Xtest
  Fallback: Disabling option --xtest"
      check_fallback
      Xtest=""
    ;;
  esac

  # --westonini
  [ -n "$Customwestonini" ] && [ ! -f "$Customwestonini" ] && {
    warning "Custom weston.ini (option --westonini) not found.
  $Customwestonini"
    check_fallback
    Customwestonini=""
  }

  # --xc
  case "$Xcontainer" in
    yes|no|auto) ;;
    docker|podman|nerdctl) ;;
    *)
      note "Option --xc: Unknown argument '$Xcontainer'.
  Fallback: Setting --xc=auto."
      check_fallback
      Xcontainer="auto"
    ;;
  esac

  return 0
}
check_options_interferences() { # check multiple option interferences, change settings if needed
  local Message

  [ "$Desktopmode" = "no" ] && case "$Xserver" in
    --xephyr|--weston-xwayland|--kwin-xwayland|--xorg|--xwayland) Windowmanagermode="auto" ;;
  esac

  case "$Xserver" in
    --xorg) # check if --xorg can run
      [ "$Autochooseserver" = "yes" ] && [ "$Codename" = "xonly" ] && error "Will not run an empty Xorg in auto-choosing mode.
  If you want this, please use option --xorg explicitly."

      [ -e "/etc/X11/Xwrapper.config" ] && sed 's/ //g' /etc/X11/Xwrapper.config | grep -xq "allowed_users=anybody" && sed 's/ //g' /etc/X11/Xwrapper.config | grep -xq "needs_root_rights=yes" && {
        Xlegacywrapper="yes"
      } || {
        Xlegacywrapper="no"
        [ "$Startuser" != "root" ] && [ "$Runsonconsole" != "yes" ] && warning "Your configuration seems not to allow to start
  a second core Xorg server from within X. Option --xorg may fail.
  (Per default, only root or console users are allowed to run an Xorg server).

  Possible solutions:
  1.) Install one of nested X servers 'Xephyr', 'Xnest' or 'nxagent'.
      For --gpu support: install 'weston' and 'Xwayland'.
  2.) Switch to console tty1...tty6 with <CTRL><ALT><F1>...<F6>
      and start x11docker there.
  3.) Run x11docker as root.
  4.) Edit file '/etc/X11/Xwrapper.config' and replace line:
          allowed_users=console
      with lines
          allowed_users=anybody
          needs_root_rights=yes
      If the file does not exist already, you can create it.
      On Debian and Ubuntu you need package xserver-xorg-legacy.

   Be aware that switching directly between Xorg servers can crash them.
   Always switch to a black console first before switching to Xorg."
      }
    ;;
    --tty)
      [ "$Interactive" = "no" ] && {
        tput lines >/dev/null 2>&1 && store_runoption env "LINES=$(tput lines)"
        tput cols  >/dev/null 2>&1 && store_runoption env "COLUMNS=$(tput cols)"
        #verbose "Option --tty: Setting LINES and COLUMNS to terminal size ${COLUMNS}x${LINES}."
      }
    ;;
    --hostdisplay)
      [ "$Winsubsystem" ] && Trusted="yes" || Trusted="no"
      [ -n "$(cut -d: -f1 <<< "$Hostdisplay")" ] && Xoverip="yes"
      [ -z "$Hostxauthority" ] && {
        note "Option --hostdisplay: You host X server seems to run
  without cookie authentication. Cannot set up a cookie for X access.
  Fallback: Enabling option --no-auth."
        check_fallback
        Xauthentication="no"
      }
      check_optionset "$Xserver" "--xtest" && {
        note "Option --xtest: Cannot enable or disable X extension XTEST
  with option $Xserver."
        check_fallback
        Xtest=""
      }
    ;;
  esac

  # check if a host window manager is needed
  [ "$Desktopmode" = "no" ] && [ -z "$Windowmanagermode" ] && case "$Xserver" in
    --xephyr|--weston-xwayland|--kwin-xwayland|--xorg|--xwayland)
      note "Option $Xserver: x11docker assumes that you need
  a window manager. If you don't want this, run with option --desktop.
  Enabling option --wm to provide a window manager."
      Windowmanagermode="auto"
      [ "$Autochooseserver" = "yes" ] && [ "$Runsonconsole" = "no" ] && {
        case "$Sharegpu" in
          no)  note "Did not find a nice solution to run a seamless application
  on your desktop. (Only insecure option --hostdisplay would work).
  It is recommended to install xpra or nxagent
  to allow a seamless mode without the need of a window manager from host." ;;
          yes) note "Did not find a nice solution to run a seamless application with
  option --gpu on your desktop. (Only insecure option --hostdisplay would work).
  It is recommended to install xpra, weston, Xwayland and xdotool
  to allow a seamless mode without the need of a window manager from host." ;;
        esac
      }
    ;;
    *)
      Windowmanagermode="none"
    ;;
  esac

  # check xauth
  [ "$Xauthentication" = "yes" ] && case "$Xserver" in
    --tty|--weston|--kwin|--hostwayland) Xauthentication="no" ;;
    *)
      [ "$Xtoolscontainer" = "no" ] && {
        command -v xauth >/dev/null || {
          case "$Xoverip" in
            yes)
              [ -z "$Hostxauthority" ] && [ "$Xserver" = "--hostdisplay" ] && Message=warning || Message=error
              $Message "Command 'xauth' not found.
      SECURITY RISK!
  Your X server would be accessible over network without authentication!
  That could be abused to take control over your system.
  Please install 'xauth' to allow X cookie authentication,
  or provide image x11docker/xserver.
  You can disable cookie authentication with discouraged option --no-auth."
            ;;
            no|"")
              warning "Command 'xauth' not found.
  Please install 'xauth' to allow X cookie authentication,
  or provide image x11docker/xserver.
  Securing X access with cookie authentication is not possible.
  Fallback: Disabling X authentication protocol. (option --no-auth)
  $Wikipackagesimage"
            check_fallback
            ;;
          esac
          Xauthentication="no"
        }
      }
    ;;
  esac

  # --fullscreen is nonsense on tty at all. Avoids weston error on tty.
  [ "$Runsonconsole" = "yes" ] && Fullscreen="no"

  # --hostdisplay --gpu
  [ "$Xserver" = "--hostdisplay" ] && [ "$Sharegpu" = "yes" ]  && [ "$Trusted" = "no" ] && {
    note "Option --gpu: To allow GPU acceleration with --hostdisplay,
  x11docker will allow trusted cookies."
    Trusted="yes"
  }

  # --hostdisplay with SSH
  [ "$Xserver" = "--hostdisplay" ] && [ "$Runsoverssh" = "yes" ] && {
    [ "$Network" != "host" ]  && {
      warning "For SSH connection with option --hostdisplay
  x11docker must enable option --network=host.
  It is recommended to use other X server options
  like --xpra, --xephyr or --nxagent."
      check_fallback
      Network="host"
    }
    [ "$Trusted" = "no" ] && {
      warning "For SSH connection with option --hostdisplay
  x11docker must allow trusted cookies.
  It is recommended to use other X server options
  like --xpra, --xephyr or --nxagent."
      Trusted="yes"
    }
  }

  # --hostdisplay with untrusted cookies: check xdpyinfo
  [ "$Xserver" = "--hostdisplay" ] && [ "$Trusted" = "no" ] && {
    command -v xdpyinfo >/dev/null && {
      xdpyinfo | grep -q SECURITY || {
        note "Your X server does not support untrusted cookies.
  Have to allow trusted cookies.
  Consider to use options --xpra or --nxagent instead of --hostdisplay."
        Trusted="yes"
      }
    } || note "Command 'xdpyinfo' not found. Need it to check
  whether Xorg supports untrusted cookies for --hostdisplay
  and whether extension MIT-SHM for shared memory is enabled.
  Please install 'xdpyinfo'.
  $Wikipackages"
  }

  # --clipboard
  case "$Shareclipboard" in
    yes)
      case "$Xserver" in
        --weston|--kwin) 
          note "Sharing clipboard with $Xserver is not supported"
          Shareclipboard="no"
        ;;
        --hostwayland) 
          note "Sharing clipboard may or may not work.
  Cannot enable or disable it, it depends on your Wayland compositor."
          Shareclipboard="no"
        ;;
        --hostdisplay)
          [ "$Trusted" = "no" ] && warning "Option --clipboard: To allow clipboard sharing with
  option --hostdisplay, trusted cookies will be enabled.
  No protection against X security leaks is left!
  Consider to use another X server option."
          Trusted="yes"
        ;;
      esac
      case "$Xserver" in
        --xpra*|--hostdisplay|--kwin|--weston|--tty|--xwin|--runx) ;;
        *) note "Sharing picture clips with option --clipboard
  is only possible with options --xpra, --xpra-xwayland and --hostdisplay." ;;
      esac
    ;;
  esac
  [ "$Trusted" = "no" ] && warning "Option --hostdisplay: Clipboard isolation might fail."

  # --gpu=virgl
  case "$Virgl" in
    yes)
      case "$Xcontainer" in
        yes)
          case "$Backend" in
            docker|podman|nerdctl) 
              store_runoption env "LIBGL_ALWAYS_SOFTWARE=1"
              store_runoption env "GALLIUM_DRIVER=virpipe"
            ;;
            *) 
              note "Option --gpu=virgl is supported with
  backends docker|podman|nerdctl only.
  Fallback: Disabling option --gpu=virgl."
              check_fallback
              Virgl=""
            ;;
          esac
        ;;
        no)
          note "Option --gpu=virgl is supported only with option --xc yet.
  Fallback: Disabling option --gpu=virgl."
          check_fallback
          Virgl=""
        ;;
      esac
    ;;
  esac

  # --no-setup
  case "$Containersetup" in
    no)
      check_optionset "--no-setup" "--dbus --no-entrypoint --runasroot --runasuser" ||:

      [ "$Langwunsch" ] && store_runoption env "LANG=$Langwunsch"

      case "$Initsystem" in
        none) ;;
        tini|dockerinit) Initsystem="dockerinit" ;;
        *) note "Option --no-setup: Option --init=$Initsystem is not supported.
  Fallback: Setting --init=tini"
          Initsystem="dockerinit"
        ;;
      esac

      [ -z "$Workdir" ] && [ "$Sharehome" != "no" ] && note "Option --no-setup: You might need to specify
  e.g. '--workdir=/tmp' or '--env HOME=/tmp' to allow proper functionality."

      [ "$Sudouser" != "no" ] && [ "$Containeruser" != "root" ] && note "Option --no-setup does not support option --sudouser.
  Fallback: Enables needed container capabilities to allow sudo
  just in case the container user is set up for su and/or sudo.
  Consider to use --user=root."

      ### FIXME check support of further options
      # --stdin?
      # --hostdbus

      Dbusrunsession="no"
      Noentrypoint="no"
      Runasroot=""
      Runasuser=""
    ;;
  esac

  # --cap-default
  [ "$Capdropall" = "no" ] && {
    warning "Option --cap-default disables security hardening
  for containers done by x11docker. Default docker capabilities are allowed.
  This is considered to be less secure."
    case "$Allownewprivileges" in
      "yes"|"no") ;;
      "auto")
        note "Option --cap-default: Enabling option --newprivileges.
  You can avoid this with --newprivileges=no"
        Allownewprivileges="yes"
      ;;
    esac
  }

  # --ipc=host: Check auto-enabling
  [ "$Xserver" = "--hostdisplay" ] && [ "$Trusted" = "yes" ] && [ "$Hostmitshm" = "yes" ] && [ "$Shareipc" != "host" ] && [ "$Runsoverssh" = "no" ] && {
    # make XlibNoSHM.so available on host                    ### FIXME maybe not the best place here
    [ "$Xcontainerimage" ] && [ ! -f "$Mitshmlib" ] && {
      note "Copying XlibNoSHM.so from x11docker/xserver 
  to $Mitshmlib
  to make it available for --hostdisplay."
      $Backendbin run --rm --user "$Hostuseruid:$Hostusergid" --volume "$Localsharedir":"$Localsharedir" -- "$Xcontainerimage" cp "/XlibNoSHM.so" "$Mitshmlib"
    }
    [ -f "$Mitshmlib" ] && {
      ### FIXME check for possible existing LD_PRELOAD in container
      store_runoption env "LD_PRELOAD=/lib/x86_64-linux-gnu/libdl.so.2:$Sharefoldercontainer/XlibNoSHM.so"
      cp "$Mitshmlib" "$Sharefolder"
    } || {
      case "$Backend" in
        docker|podman|nerdctl)
          note "Option --hostdisplay: To allow --hostdisplay with trusted cookies,
  x11docker must share host IPC namespace with container (option --ipc=host)
  to allow shared memory for X extension MIT-SHM.
  You can avoid this if you provide image x11docker/xserver
  that contains an X library XlibNoSHM.so to avoid MIT-SHM issues."
          case $Shareipc in
            no|host) 
              Shareipc="host"
            ;;
            *) 
              note "Option --ipc: Did not change IPC settings due to 
  custom setup. Issues noted above might occure."
            ;;
          esac
        ;;
      esac
    }
  }

  # --keymap: XKB keyboard layout
  [ -n "$Xkblayout" ] && {
    case "$Xserver" in
      --kwin|--kwin-xwayland)
        [ "$Runsonconsole" = "yes" ] && {
          check_optionset "$Xserver" "--keymap" && {
            note "Option --keymap does not work with option $Xserver
  if running from console.
  Fallback: disabling option --keymap."
            check_fallback
            Xkblayout=""
          }
        }
      ;;
    esac
    [ "$Xkblayout" = "clone" ] && case "$Xserver" in
      --nxagent) ;;
      *)
        note "--keymap=clone is supported with --nxagent only.
  Fallback: Disabling option --keymap."
        check_fallback
        Xkblayout=""
      ;;
    esac
  }

  # --scale
  [ "$Scaling" ] && {
    case "$Xserver" in
      --weston|--weston-xwayland)
        [[ $Scaling =~ ^[1-9]$ ]] || {
          note "The scale factor for option $Xserver must be
  one of   1  2  3  4  5  6  7  8  9
  Fallback: disabling option --scale"
          check_fallback
          Scaling=""
        }
      ;;
      *)
        note "Option $Xserver does not support option --scale.
  Available for --xpra* and --xorg (float values possible)
  and for --weston and --weston-xwayland (full integer values only).
  Fallback: disabling option --scale"
        check_fallback
        Scaling=""
      ;;
    esac
    case "$Xserver" in
      --weston-xwayland) note "Weston does not work well with Xwayland in scaled mode.
  In summary, Xwayland does not get the right screen resolution from Weston.
  (Bug report at https://bugzilla.redhat.com/show_bug.cgi?id=1498669 ).
  Try out if it works for you. Otherwise, you can combine
  '--xpra-xwayland --desktop --scale $Scaling' for better desktop scaling support.
  --scale for single applications works best with --xpra / --xpra-xwayland.
  --scale in desktop mode works best with option --xorg."
      ;;
      --xpra-xwayland|--xpra2-xwayland)
        [ "1" = "$(awk -v a="${Scaling:-1}" 'BEGIN {print (a < 1)}')" ] && {
          [ "$Xcontainer" = "no" ] && {
            command -v weston >/dev/null || {
              note "Option $Xserver needs weston
  for scale factor smaller than 1.
  Fallback: disabling option --scale"
              check_fallback
              Scaling=""
            }
          }
        }
      ;;
      --xorg)
        [ "1" = "$(awk -v a="$Scaling" 'BEGIN {print (a < 1)}')" ] && [ -n "$Rotation" ] && note "--xorg does not work well with combination
  of --scale smaller than 1 and rotation different from 0."
      ;;
    esac
  }

  # --rotate
  [ -n "$Rotation" ] && {
    case "$Xserver" in
      --weston|--weston-xwayland|--xorg) ;;
      *)
        note "Option $Xserver does not support option --rotate.
  Rotation is possible for --xorg, --weston and --weston-xwayland.
  Fallback: disabling option --rotate"
        check_fallback
        Rotation=""
      ;;
    esac
  }
  [ "$Rotation" = "0" ] && Rotation="normal"

  # xrandr: --scale --size --rotate
  command -v xrandr >/dev/null || case "$Xserver" in
    --xorg) { [ "$Scaling" ] || [ -n "$Rotation" ] || [ -n "$Screensize" ] ; } && note "Option --xorg needs 'xrandr' to support
  options --size, --scale and --rotate.
  Please install 'xrandr'.
  $Wikipackagesimage"
      check_fallback
    ;;
  esac

  # --dpi
  [ -n "$Dpi" ] && case "$Xserver" in
    --weston|--kwin|--hostwayland|--hostdisplay)
      note "Option --dpi has no effect with option $Xserver"
      check_fallback
      Dpi=
    ;;
  esac

  # --output-count
  [ "$Outputcount" != "1" ] && {
    case "$Xserver" in
      --xephyr|--weston|--kwin|--weston-xwayland|--kwin-xwayland|--xwin)
        [ "$Runsonconsole" = "yes" ] && {
          note "Option --outputcount only works in nested/windowed mode,
  but not on tty. Fallback: disabling --outputcount"
          check_fallback
          Outputcount="1"
        }
      ;;
      *) note "$Xserver does not support option --output-count.
  Only available for Weston, KWin and Xephyr, thus for options
  --weston, --weston-xwayland, --kwin, --kwin-xwayland, --xephyr."
        check_fallback
        Outputcount="1"
      ;;
    esac
  }

  # --xfishtank: fish tank
  [ "$Xfishtank" = "yes" ] && [ "$Xcontainer" = "no" ] && {
    command -v xfishtank >/dev/null || {
      note "xfishtank not found. Can not show a fish tank.
  Please install 'xfishtank' for option --xfishtank to show a fish tank.
  $Wikipackagesimage"
      check_fallback
      Xfishtank="no"
    }
    case "$Xserver" in
      --xpra*|--nxagent)
        [ "$Desktopmode" = "no" ] && [ "$Windowmanagermode" = "none" ] && Windowmanagermode="auto" && Desktopmode="yes" ;;
      --weston|--kwin|--hostwayland|--hostdisplay|--tty)
        note "Option --xfishtank is not supported for $Xserver."
        check_fallback
        Xfishtank="no"
      ;;
    esac
  }

  # MSYS2, Cygwin, WSL
  case "$Winsubsystem" in
    WSL2) note "WSL2 support is experimental and barely tested yet.
  Feedback and bug reports are appreciated!" ;;
  esac
  case "$Mobyvm" in
    yes)
      case "$Winsubsystem" in
        WSL1|WSL2)
          grep -q "/c/" <<< "$Cachebasefolder" && [ -z "$Hosthomebasefolder" ] && note "With MobyVM and WSL x11docker stores its cache files on drive C:
  to allow cache file sharing.
  Your Docker setup might not allow to share files from drive C:.
  If startup fails with an 'access denied' error,
  please either allow access to drive C: or specify a custom folder for
  cache storage with option '--cachebasedir D:/some/cache/folder'.
  Same issue can occur with option '--home'.
  Use option '--homebasedir D:/some/home/folder' in that case.
  Option --preset might help you to reduce typing on each command."
        ;;
      esac
      [ "$Initsystem" = "systemd" ] && {
        note "Option --init=systemd is not supported with MobyVM.
  You can try another init option instead, e.g. --init=openrc.
  Fallback: Disabling option --init=systemd"
        check_fallback
        Initsystem="tini"
      }
      [ "$Sharecgroup" = "yes" ] && {
        note "Option --sharecgroup is not supported with MobyVM.
  Fallback: Disabling option --sharecgroup."
        check_fallback
        Sharecgroup="no"
      }
    ;;
  esac
  case "$Winsubsystem" in
    MSYS2|CYGWIN|WSL1|WSL2)
      check_optionset "Windows" "--pulseaudio --printer --webcam" ||:
      Sharecupsmode=""
      Pulseaudiomode=""
      Sharewebcam="no"
      case "$Xserver" in
        --xwin|--runx) note "Windows firewall settings can forbid application access
  to the X server. If no application window appears, but no obvious error
  is shown, please check your firewall settings. Compare issue #108 on github." ;;
      esac
    ;;
  esac

  # --wayland
  [ "$Setupwayland" = "yes" ] && case "$Xserver" in
    --weston|--kwin|--hostwayland) 
      Dbusrunsession="yes"
    ;;
    *)
      note "Option --wayland: Sharing Wayland socket is not supported
  for X server option $Xserver.
  You can try --weston, --kwin or --hostwayland instead.
  Fallback: Disabling option --wayland."
      check_fallback
      Setupwayland="no"
    ;;
  esac

  # --init
  case "$Initsystem" in
    systemd|sysvinit|openrc|runit) Dbussystem="yes" ;;
  esac

  # --interactive
  case "$Interactive" in
    yes)
      case "$Winsubsystem" in
        MSYS2|CYGWIN|WSL1)
          Winpty="$(command -v winpty)"
          Winpty="$(escapestring "$Winpty")"
          [ "$Winpty" ] || error "Option -i, --interactive: On MS Windows you need 'winpty'
  to run x11docker in interactive mode. MSYS2 provides winpty as a package.
  On Cygwin it can be compiled from source. WSL1 isn't supported yet.
  WSL2 might work, but is not tested yet."
        ;;
      esac
      [ "$Forwardstdin" = "yes" ] && {
        note "You cannot use --stdin along with --interactive.
  Fallback: Disabling option --stdin."
        check_fallback
        Forwardstdin="no"
      }
      [ "$Runsinteractive" = "yes" ] && {
        note "Option -i, --interactive: Does not work in interactive
  bash mode (option --enforce-i).
  Fallback: Disabling option --interactive."
        check_fallback
        Interactive="no"
      }
      case "$Initsystem" in
        openrc|runit|sysvinit)  note "Option --interactive: Interactive mode with option
  --init=$Initsystem is not well integrated yet.
  Shells do not have job control and CTRL-C can behave different than expected." ;;
      esac
    ;;
  esac
  [ "$Interactive" = "yes" ] && Showcontaineroutput="no"

  case "$Runtime" in
    ""|runc|crun|oci) ;;
    sysbox-runc)
      case "$Backend" in
        docker) ;;
        *) note "Option --runtime=$Runtime --backend=$Backend: $Runtime is known
  to work with Docker only yet." ;;
      esac
      store_runoption env "SYSBOX_HONOR_CAPS=TRUE"
      check_optionset "--runtime=sysbox-runc" "--alsa --webcam" || check_fallback
      verlt "$(uname -r)" "5.12" && error "Option --runtime=sysbox-runc needs at least
  Linux kernel version >=5.12"
      verlt "$(sysbox-runc -v | grep version | tr -d ' \t'| cut -d: -f2)" "0.5" && error "Option --runtime=sysbox-runc needs at least
  Sysbox version >=0.5.0"
      [ "$Sharegpu" = "yes" ] && note "Option --gpu with option --runtime=$Runtime:
  GPU access might fail for some applications."
    ;;
    nvidia)
      [ "$Sharegpu" = "no" ] && {
        note "Option --runtime=nvidia: Enabling option --gpu"
        check_fallback
        Sharegpu="yes"
      }
    ;;
    kata-runtime)
      note "Option --runtime=kata-runtime: Be aware not to share
  the same files with runc and kata-runtime containers at the same time.
  Otherwise container startup may fail."
      [ "$Network" = "host" ] && {
        note "Option --network=host: Sharing host network stack does not work
  with --runtime=kata-runtime. Fallback: Setting --network=bridge."
        check_fallback
        Network="bridge"
      }
      [ "$Shareipc" = "host" ] && {
        note "Option --ipc=host: Only IPC of the qemu VM is shared with
  --runtime=kata-runtime."
      }
      check_optionset "--runtime=kata-runtime" "--alsa --webcam" ||:
      Sharewebcam="no"
      Sharealsa="no"
    ;;
    *)
      note "Option --runtime: x11docker does not know runtime: $Runtime
  Will try to use it anyway.
  If that fails, you can try options --snap or --no-setup."
    ;;
  esac

  return 0
}
check_options_messages() {      # some messages depending on options, but not changing settings
  # -I, --network
  case "$Network" in
    none) note "Option --network=none is set to disable network access.
  If you need network and internet access, set option -I, --network." ;;
  esac
  # X server specific messages
  case "$Xserver" in
    --hostdisplay)
      [ "$Autochooseserver" = "yes" ] && [ -z "$Winsubsystem" ] && case "$Sharegpu" in
        yes)
          note "To allow protection against X security leaks,
  please install 'xinit' and one or more of:
    xpra, weston+Xwayland or kwin_wayland+Xwayland,
  or run a second Xorg server with option --xorg." ;;
        no) note "To allow protection against X security leaks,
  please install 'xinit' and one or more of:
    xpra, Xephyr, nxagent, weston+Xwayland, kwin_wayland+Xwayland or Xnest,
  or run a second Xorg server with option --xorg." ;;
      esac
      case "$Trusted" in
        no)
          warning "Option --hostdisplay provides only low container isolation!
  It is recommended to use another X server option like --nxagent or --xpra.

  To improve security with --hostdisplay x11docker uses untrusted cookies.
  This can lead to strange behaviour of some applications.

  If you encounter application ${Colredbg}errors${Colnorm}, enable option --clipboard
  that disables security restrictions for --hostdisplay as a side effect." ;;
        yes)
          case "$Winsubsystem" in
            "") warning "Option --hostdisplay with trusted cookies provides
      QUITE BAD CONTAINER ISOLATION !
  Keylogging and controlling host applications is possible!
  Clipboard sharing is enabled (option --clipboard).
  It is recommended to use another X server option like --nxagent or --xpra." ;;
            MSYS2) ;;
            CYGWIN) warning "Option --hostdisplay allows less security hardening.
  It is recommended to use option --xwin instead." ;;
            WSL1|WSL2) warning "Option --hostdisplay allows less security hardening.
  It is recommended to use another X server option like --nxagent or --xephyr." ;;
          esac
        ;;
      esac
      [ "$Desktopmode" = "yes" ] && note "Can not avoid to use host window manager
  along with option --hostdisplay.
  You may get strange interferences with your host desktop.
  Can be interesting though, having two overlapping desktops."
    ;;

    --xorg)
      [ "$Hostsystem" = "opensuse" ] && [ "$Runsonconsole" = "no" ] && [ "$Startuser" != "root" ] && warning "openSUSE does not support starting a second Xorg server
  from within X. Possible solutions:
  1.) Install nested X server 'Xephyr', 'nxagent' or 'Xnest',
      or for --gpu support: install 'Weston' and 'Xwayland'.
  2.) Switch to console tty1...tty6 with <CTRL><ALT><F1>...<F6>
      and start x11docker there.
  3.) Run x11docker as root."

      case "$Xlegacywrapper" in
        yes) warning "Although x11docker starts Xorg as unprivileged user,
  most system setups wrap Xorg to give it root permissions (setuid).
  Evil containers may try to abuse this.
  Other x11docker X server options like --xephyr are more secure at this point." ;;
        no) [ "$Startuser" = "root" ] && warning "x11docker will run Xorg as root." ;;
      esac

      [ "$Runsoverssh" = "yes" ] && warning "x11docker can run Xorg on another tty (option --xorg),
  but you won't see it in your SSH session.
  Rather install e.g. Xephyr on ssh server and use option --xephyr."

      # --gpu=iglx
      Xorgversion="$(grep -m1 xorg-server </var/log/Xorg.0.log | cut -d: -f2 | cut -d' ' -f1)"
      [ "$Iglx" = "yes" ] && [ -z "$Nvidiaversion" ] && [ -n "$Xorgversion" ] && verlt "$Xorgversion" "1.20.8" && note "Options --xorg --gpu=iglx: It seems you have Xorg v$Xorgversion.
  iGLX is known to have a bug in Xorg<v1.20.8.
  --gpu=iglx may fail and give black content."
    ;;

    --xpra*)
      [ "$Sharegpu" = "yes" ] && note "Option --$Xserver: If performance of GPU acceleration
  is not satisfying, you can try insecure '--hostdisplay --gpu'
  or, better but windowed, '--weston-xwayland --gpu'."
      note "Option $Xserver: If you encounter issues with xpra,
  you can try --nxagent instead.
  Rather use xpra from www.xpra.org than from distribution repositories."
    ;;

    --nxagent)
      [ "$Hostsystem" = "mageia" ] && {
        [ "$Desktopmode" = "no" ] && [ "$Autochooseserver" = "yes" ] && Desktopmode="yes" && Windowmanagermode="auto"
        [ "$Desktopmode" = "no" ] && warning "nxagent version 3.5.0 on Mageia 6 is known to crash
  in seamless mode. (Detected version: '$(strings --bytes 20 /usr/libexec/nx/nxagent | grep "NXAGENT - Version")').
  If you encounter issues, please try seamless --xpra (secure),
  --hostdisplay (insecure), or run --nxagent in desktop mode with a
  host window manager (--wm=WINDOWMANAGER or --wm=auto or short -wm)."
      }
      note "A few applications do not work well with --nxagent.
  In that case, you can try to fix the issue with option --composite
  or try another X server option like --xephyr or --xpra."
      [ "$Xcomposite" = "yes" ] && note "Option --nxagent: nxagent can have issues with option
  --composite. Maybe rather try --xephyr or --xpra."
    ;;

    --weston|--kwin|--hostwayland)
      note "You are running a pure Wayland environment.
  X applications without Wayland support will fail."
      [ "$Xserver" = "--kwin" ] && note "kwin_wayland (option --kwin) does not support the xdg_shell
  interface in all versions. Some GTK3 Wayland applications depend on it.
  If application startup fails, try --weston instead."
    ;;
  esac

  # NVIDIA without --gpu
  [ "$Nvidiaversion" ] && [ "$Sharegpu" = "no" ] && case "$Xserver" in
    --hostdisplay|--xorg) note "Option $Xserver may fail with proprietary NVIDIA driver
  on host without enabling option --gpu.
  In that case try other X server options like --nxagent, --xpra or --xephyr." ;;
  esac

  # --gpu=iglx
  [ "$Iglx" = "yes" ] && {
    [ -z "$Nvidiaversion" ] && note "Option --gpu=iglx: iGLX is known to work only with new
  MESA drivers and new X servers. Otherwise you might get black content only."
    case "$Xserver" in
      --xorg) ;;
      *) note "Option --gpu=iglx is useful with option --xorg only." ;;
    esac
  }

  # --fullscreen
  [ "$Fullscreen" = "yes" ] && {
    case "$Xserver" in
      --xephyr|--weston|--weston-xwayland|--nxagent|--xpra*|--xwin) ;;
      --xvfb|--xorg) ;;
      *) note "Option --fullscreen is not supported by $Xserver." ;;
    esac
  }

  # --output-count
  [ "$Outputcount" != "1" ] && {
    case "$Xserver" in
      --weston-xwayland) note "Option --output-count: Xwayland sometimes does not position itself well
  at origin 0+0 of first virtual screen, and some screens appear to be unused.
  You may need to move Xwayland manually with [META]+[LeftMouseButton].
  (Bug report at https://bugzilla.redhat.com/show_bug.cgi?id=1498665 )" ;;
      --xephyr) note "Option --output-count: Xinerama support would be best for multiple outputs,
  but is disabled in Xephyr because Xephyr does not handle it well.
  Different window managers handle this different. Just try out." ;;
    esac
  }

  # --ipc=host
  [ "$Shareipc" = "host" ] && {
    case "$Backend" in
      docker|podman|nerdctl)
        warning "Option --ipc=host severely degrades
  container isolation. IPC namespace remapping is disabled."
      ;;
    esac
  }

  # --limit N
  [ "$Limitresources" ] && note "Option --limit does not avoid possibly flooding the hard disk
  in docker's container partition or in shared folders.
  It only restricts memory and CPU usage."

  # --network
  case "$Backend" in
    host) ;;
    proot|chroot) ;;
    docker|podman|nerdctl)
      case "$Network" in
        none) ;;
        ""|bridge)
          warning "Option --network: Container has access to
  local network and internet." ;;
        host)
          warning "Option --network=host severely degrades
  container isolation. Network namespacing is disabled.
  Container shares host network stack.
  Spying on network traffic may be possible.
  Access to host X server $Hostdisplay may be possible
  through abstract unix socket." ;;
        *)
          warning "Option --network=$Network: Some sort of network access
     is enabled." ;;
      esac
    ;;
  esac

  # --sudouser
  [ "$Sudouser" ] && [ "$Containeruseruid" != "0" ] && [ "$Network" != "host" ] && [ -z "$Xoverip" ] && note "Option --sudouser: If you want to run GUI application
  with su or sudo, you might need to add either option --xoverip
  or (discouraged) option --network=host."

  # --user=RETAIN / keep container user defined in image
  case "$Createcontaineruser" in
    no)
      [ "$Sudouser" ] && note "Option --sudouser has limited support with --user=RETAIN.
  x11docker will only set needed capabilities.
  User setup and /etc/sudoers won't be touched.
  Option --group-add=sudo might be useful."
    ;;
  esac

  [ "$Customdockeroptions" ] && {
    warning "Found custom CUSTOM_RUN_OPTIONS.
  x11docker will add them to 'docker run' command without
  a serious check for validity or security. Found options:
  $Customdockeroptions"
    grep -q -- '--privileged'           <<< "$Customdockeroptions" && warning "Found option --privileged
  in CUSTOM_RUN_OPTIONS. That is A VERY BAD IDEA.
  A privileged setup allows unrestricted access from container to host.
  Malicious applications can cause arbitrary harm."
    grep -q -i -- '--cap-add.ALL'       <<< "$Customdockeroptions" && warning "Found option --cap-add=ALL
  in CUSTOM_RUN_OPTIONS. That is A VERY BAD IDEA.
  That is a very privileged setup.
  Malicious applications may harm to the host."
    grep -q -i -- '--cap-add.SYS_ADMIN' <<< "$Customdockeroptions" && warning "Found option --cap-add=SYS_ADMIN
  in CUSTOM_RUN_OPTIONS. That is A VERY BAD IDEA.
  That is a very privileged setup.
  Malicious applications may harm to the host."
    grep -q -- '--entrypoint'           <<< "$Customdockeroptions" && warning "Found option --entrypoint
  in CUSTOM_RUN_OPTIONS. x11docker uses this option, too.
  This setup will probably fail. Use x11docker option --no-entrypoint instead
  and add desired command as container command after the image name."
    for Line in "--group-add" "--ipc" "--name" "--network" "--user=" "--runtime" "--workdir"; do
      grep -q -- "$Line" <<< "$Customdockeroptions" && note "Found option $Line in CUSTOM_RUN_OPTIONS.
  Please use x11docker option $Line instead.
  Otherwise x11docker might behave wrong."
    done
  }

  return 0
}
check_optionset() {             # check if unallowed options in $2 are set for option $1
  local Line Return=
  for Line in ${2:-}; do
    grep -q -x -- "$Line" <<< "$Optionset" && {
      Return=0
      note "Option ${1:-} does not support option $Line"
      check_fallback
    }
  done
  return "${Return:-1}"
}

#### main
declare_variables() {           # declare global variables
  export IFS=$' \n\t'                             # set IFS to default

  # Global environment variables used in x11docker
  ALSA_CARD=$(check_envvar "${ALSA_CARD:-}")
  CUPS_SERVER=$(check_envvar "${CUPS_SERVER:-}")
  DBUS_SESSION_BUS_ADDRESS=$(check_envvar "${DBUS_SESSION_BUS_ADDRESS:-}")
  DISPLAY=$(check_envvar "${DISPLAY:-}")
  DOCKER_HOST=$(check_envvar "${DOCKER_HOST:-}")
  GDK_BACKEND=$(check_envvar "${GDK_BACKEND:-}")
  HOME=$(check_envvar "${HOME:-}")
  LANG=$(check_envvar "${LANG:-}")
  LC_ALL=$(check_envvar "${LC_ALL:-}")
  PATH="$(check_envvar -w "${PATH:-}")"
  WAYLAND_DISPLAY=$(check_envvar "${WAYLAND_DISPLAY:-}")
  XAUTHORITY=$(check_envvar "${XAUTHORITY:-}")
  XDG_CURRENT_DESKTOP=$(check_envvar "${XDG_CURRENT_DESKTOP:-}")
  XDG_RUNTIME_DIR=$(check_envvar "${XDG_RUNTIME_DIR:-}")
  XDG_VTNR=$(check_envvar "${XDG_VTNR:-}")
  __GLX_VENDOR_LIBRARY_NAME="$(check_envvar "${__GLX_VENDOR_LIBRARY_NAME:-}")"
  __NV_PRIME_RENDER_OFFLOAD="$(check_envvar "${__NV_PRIME_RENDER_OFFLOAD:-}")"
  __VK_LAYER_NV_optimus="$(check_envvar "${__VK_LAYER_NV_optimus:-}")"

  # Add possibly missing PATH entries
  PATH="${PATH:-"/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin:/usr/local/games:/usr/games"}"
  grep -q ':/sbin:'            <<< ":$PATH:" || PATH="$PATH:/sbin"
  grep -q ':/usr/sbin:'        <<< ":$PATH:" || PATH="$PATH:/usr/sbin"
  grep -q ':/usr/local/bin:'   <<< ":$PATH:" || PATH="$PATH:/usr/local/bin"
  grep -q ':/usr/bin:'         <<< ":$PATH:" || PATH="$PATH:/bin"
  grep -q ':/usr/bin:'         <<< ":$PATH:" || PATH="$PATH:/usr/bin"
  grep -q ':/usr/local/games:' <<< ":$PATH:" || PATH="$PATH:/usr/local/games"
  grep -q ':/usr/games:'       <<< ":$PATH:" || PATH="$PATH:/usr/games"
  export PATH

  # File descriptors
  FDcmdstdin=""                                   # --stdin channel to forward stdin to container. Previously &7
  FDmessage=""                                    # message channel for notes, warnings and verbosity across threads and container. Previously &6
  #FDstderr=""                                    # internal stderr >&2, redirected to null with --silent. Already declared in main(). Previously &3
  FDtimetosaygoodbye=""                           # message channel to send termination signal from or to containers. Previously &8
  FDwatchpid=""                                   # message channel for watchpidlist(). Previously &9

  # Terminal colors used for messages and -V
  Esc="$(printf '\033')"
  Colblue="${Esc}[35m"
  Colyellow="${Esc}[33m"
  Colgreen="${Esc}[32m"
  Colgreenbg="${Esc}[42m"
  Colred="${Esc}[31m"
  Colredbg="${Esc}[41m"
  Coluline="${Esc}[4m"
  Colnorm="${Esc}[0m"

  # x11docker startup environment
  Runsinsnap=""                                   # docker runs in Ubuntu snap yes/no
  Runsinteractive=""                              # --enforce-i: Script runs in bash interactive mode (bash -i) yes/no.
  Runsinterminal=""                               # x11docker runs in a terminal yes/no
  Runsonconsole=""                                # x11docker runs on tty yes/no
  Runsoverssh=""                                  # x11docker runs over SSH yes/no. Makes a difference for --hostdisplay
  Runssourced=""                                  # x11docker has been sourced yes/no

  # Generated scripts
  Clipboardrc="clipboardrc"                       # --clipboard: Generated script for text clipboard sharing
  Cmdrc="cmdrc"                                   # Generated script starting container command
  Containerrc="containerrc"                       # Generated script starting cmdrc
  Containerrootrc="containerrootrc"               # Generated script to set up container, e.g. user creation. Runs as root in container.
  Xinitrc="xinitrc"                               # Generated script to set up X, e.g. cookie and xrandr
  Xtermrc="xtermrc"                               # Generated script for password prompt

  # Internal messages
  Logmessages=""                                  # Stores messages until logfile is available, needed by logentry()
  Messagefifo="message.fifo"                      # Message channel for warning/verbose/debugnote/note/error within container,, containerrootrc and others
  Storeinfofile="store.info"                      # File to store some info like id, pid, name, exit code
  Storepidfile="store.pids"                       # File to store pids and names of background processes that should be terminated on exit
  Timetosaygoodbyefile="timetosaygoodbye"         # File giving term signal to all parties
  Timetosaygoodbyefifo="timetosaygoodbye.fifo"    # Message channel for --init=openrc|runit|sysvinit to shut down on x11docker signal
  Usemkfifo=""                                    # Not on Windows nor with kata-runtime
  Watchpidfifo="watchpid.fifo"                    # Message channel to transfer pids to watchpidlist()

  # Logfiles
  Backendcommandfile="docker.command"              # File to store generated docker command, needed for --interactive
  Compositorlogfile="compositor.log"              # Logfile for weston or kwin_wayland
  Containerlogfile="container.log"                # Logfile for container output other than container command output
  Logfile=""                                      # $Cachefolder/x11docker.log (current log)
  Logfilebackup=""                                # $Cachebasefolder/x11docker.log (latest terminated log)
  Messagelogfile="message.log"                    # Logfile for warning/verbose/debugnote/note/error
  Xinitlogfile="xinit.log"                        # Logfile for xinit/X server
  Xpraclientlogfile="xpra.client.log"             # Logfile for xpra client
  Xpraserverlogfile="xpra.server.log"             # Logfile for xpra server

  # Generated commands
  Compositorcommand=""                            # Command to start Weston or KWin
  Backendcommand=""                                # Command to run docker
  Xcommand=""                                     # Command to start X server
  Xpraclientcommand=""                            # xpra client command
  Xpraservercommand=""                            # xpra server command

  # Users
  Hostuser=""                                     # $Lognameuser or --hostuser. Unprivileged user for non-root commands. Compare unpriv()
  Hostusergid=""
  Hostuserhome=""
  Hostuseruid=""
  Containeruser=""                                # --user: Container user. Default: same as $Hostuser.
  Containeruseruid=""
  Containerusergid=""
  Containerusergroup=""
  Containerusergroups=""                          # --group-add: Additional groups for container user
  Containeruserhome=""                            # HOME path within container
  Containeruserhosthome=""                        # HOME path of container user on host
  Containeruserpassword=''
  Createcontaineruser="yes"                       # exception: --user=RETAIN
  Lognameuser=""                                  # $(logname) or $SUDO_USER or $PKEXEC_USER
  Passwordfile="$HOME/.config/x11docker/passwd"
  Persistanthomevolume=""                         # --home: Path to shared host folder or docker volume used as HOME in container.
  Startuser=""                                    # User who started x11docker
  Unpriv=""                                       # Command to run commands as unprivileged user
  Containerusershell="auto"                       # --shell: Preferred user shell

  # Hostsystem
  Cgroupversion=""                                # Needed for --init=systemd
  Hostarchitecture=""                             # uname -m, checked
  Hostcanwatchroot=""                             # x11docker can watch root processes yes/no. Related to $Hosthidepid
  Hostdisplay=""                                  # Environment variable DISPLAY
  Hostdisplaynumber=""                            # DISPLAY without : (and without possible IP)
  Hosthidepid=""                                  # /proc is mounted with hidepid=2 yes/no. Seen on NixOS.
  Hostip=""                                       # An IP address to access host. Preferred: IP of docker daemon
  Hostlibc=""                                     # glibc or musl. Can be important for locale and timezone.
  Hostlocaltimefile=""                            # Time zone from host, myrealpath /etc/localtime
  Hostmitshm="yes"                                # X on host has extension MIT-SHM enabled yes/no. Assume yes, check later
  Hostsystem=""                                   # $ID from /etc/os-release
  Hostutctime=""                                  # Time zone from host as offset to UTC
  Hostwaylandsocket="$WAYLAND_DISPLAY"            # Store host wayland socket name
  Hostxauthority="Xauthority.host.$(unspecialstring "${DISPLAY:-unknown}")"   # File to store copy of $XAUTHORITY
  Hostxenv=""                                     # Collection of host X environment variables
  Hostxsocket=""                                  # Socket of DISPLAY in /tmp/.X11-unix
  Mitshmlib=""                                    # local copy of XlibNoSHM.so
  Nvidiacontainerfile="/usr/local/bin/NVIDIA-installer.run"  # --gpu: Path to nvidia installer in container
  Nvidiaversion=""                                # --gpu: Proprietary nvidia driver version on host
  Nvidiainstallerfile=""                          # --gpu: Proprietary nvidia driver installer for container in [...]local/share/x11docker
  Pythonbin=""                                    # path to python binary

  # MS Windows
  Winpty=""                                       # Path to winpty for --interactive on MS Windows
  Winsubmount=""                                  # Path within subsystem to mounted MS Windows drives
  Winsubpath=""                                   # Path within MS Windows to subsystem files
  Winsubsystem=""                                 # MS Windows subsystem WSL1, WSL2, MSYS2 or CYGWIN
  Mobyvm=""                                       # MS Windows: Use MobyVM yes/no (No only for WSL2 possible)

  # Cache folders
  Cachebasefolder=""                              # --cachebasedir Base cache folder
  Cachefolder=""                                  # Subfolder of $Cachebasefolder for current container
  Localsharedir=""
  Sharefolder="share"                             # Subfolder of $Cachefolder for cache files shared with container
  Sharefoldercontainer="/x11docker"               # Mountpoint of $Sharefolder in container

  # stdin stdout stderr
  Cmdstdinfifo="stdin"                            # stdin for container command. fifo/named pipe to forward stdin of x11docker to container command
  Cmdstderrlogfile="stderr"                       # stderr for container command
  Cmdstdoutlogfile="stdout"                       # stdout for container command
  Forwardstdin="no"                               # --stdin: forward stdin to container command yes/no

  # X and Wayland configuration
  Autochooseserver="yes"                          # --auto: automatic choice of X server (default)
  Cleanxhost="no"                                 # --clean-xhost: remove xhost access policies on host X
  Compositorerrorcodes="Failed to process Wayland|failed to create display|] fatal:"
  Desktopmode="no"                                # --desktop: image contains a desktop environment.
  Dpi=""                                          # --dpi: dots per inch. Influences font size
  Fullscreen="no"                                 # --fullscreen: Fullscreen mode
  Iglx=""                                         # --gpu=iglx: Enable indirect rendering yes/no/""
  Lastcheckedxserver=""                           # check_xdepends(): Last X server option that was checked
  Lastcheckedxserverresult=""                     # check_xdepends(): Result of last check. Avoids double-checking.
  Maxxaxis=""                                     # Maximal X screen size of display
  Maxyaxis=""                                     # Maximal Y screen size of display
  Modelinefilebasepath="modelines"
  Newdisplay=""                                   # --display: New DISPLAY for new X server
  Newdisplaynumber=""                             # --display: New display number for new X server.
  Newwaylandsocket=""                             # Wayland socket of $Compositorcommand
  Newxenv=""                                      # Environment variables for new X server: DISPLAY XAUTHORITY XSOCKET WAYLAND_DISPLAY XDG_RUNTIME_DIR
  Newxenv_cr=""                                   # Like Newxenv, but with newlines
  Newxlock=""                                     # .Xn-lock - exists for running X server with socket n
  Newxsocket=""                                   # New X unix socket
  Newxvt=""                                       # --vt: number of virtual console to use for --xorg
  Numbersinusefile="displaynumbers.$(date +%y_%m_%d)" # File to store display numbers used today. Helps to avoid race conditions on simultaneous startups
  Nxagentclientrc="nxagent.nxclientrc"            # --nxagent NX_CLIENT script to catch nxagent messages
  Nxagentkeysfile="nxagent.keys"                  # --nxagent keyboard shortcut config
  Nxagentoptionsfile="nxagent.options"            # --nxagent options not available on cli, but possible in config file
  Modeline=""                                     # Screen modeline describing display size, see "man cvt".
  Outputcount="1"                                 # --output-count: quantum of virtual screens for Weston or Xephyr
  Rotation=""                                     # --rotate: Rotation for --weston, --weston-xwayland or --xorg: 0/90/180/270/flipped/flipped-90/..
  Scaling=""                                      # --scale: Scaling factor for xpra and weston
  Screensize=""                                   # --size XxY: Display size
  Setupwayland="no"                               # --wayland, --kwin, --weston --hostwayland: Provide a Wayland environment
  Trusted="yes"                                   # Create trusted or untrusted cookies,  --hostdisplay uses untrusted cookies by default
  Waylandtoolkitenv="XDG_SESSION_TYPE=wayland GDK_BACKEND=wayland QT_QPA_PLATFORM=wayland CLUTTER_BACKEND=wayland SDL_VIDEODRIVER=wayland ELM_DISPLAY=wl ELM_ACCEL=opengl ECORE_EVAS_ENGINE=wayland_egl"
  Xauthentication="yes"                           # --no-auth: use cookie authentication and disable xhost yes/no
  Xaxis=""                                        # Virtual screen width
  Xcomposite=""                                   # --xcomposite: +extension COMPOSITE yes/no
  Xkblayout=""                                    # --keymap: Layout for keymap, compare /usr/share/X11/xkb/symbols
  Xfishtank="no"                                  # --xfishtank: Show a fish tank on new X server
  Xhost=""                                        # --xhost: custom xhost setting on new X server
  Xiniterrorcodes="xinit: giving up|unable to connect to X server|Connection refused|server error|Only console users are allowed"
  Xlegacywrapper=""                               # --xorg: /etc/X11/Xwrapper.config is configured to run within X yes/no
  Xpraborder=""                                   # --border: Colored border for xpra clients
  Xpracontainerenv="UBUNTU_MENUPROXY= QT_X11_NO_NATIVE_MENUBAR=1 MWNOCAPTURE=true MWNO_RIT=true MWWM=allwm GTK_OVERLAY_SCROLLING=0 GTK_CSD=0 NO_AT_BRIDGE=1" # environment variables
  Xprahelp=""                                     # Output of 'xpra --help'
  Xprashm=""                                      # xpra server should use MIT-SHM yes/no
  Xpraversion=""                                  # $(xpra --version) to decide some xpra options and messages
  Xserver=""                                      # X server option to use
  Xoverip=""                                      # --xoverip: Connect to X over TCP yes/no
  Xserveroptions=""                               # --xopt: Custom X server options
  Xtest=""                                        # --xtest: Enable extension Xtest yes/no. If empty, yes for --xpra/--xvfb, otherwise no
  Yaxis=""                                        # Virtual screen height

  # X and Wayland config and cookie files
  Customwestonini=""                              # --westonini: Custom config file for weston
  Westonini="weston.ini"                          # Generated config file for weston
  Xclientcookie="Xauthority.client"               # Generated X client cookie. Normally same as $Xservercookie, except for --hostdisplay and --nxagent
  Xkbkeymapfile="xkb.keymap"                      # --keymap: File to store output of host keymap in xinitrc
  Xorgconf=""                                     # --xorgconf: custom xorg.conf
  Xservercookie="Xauthority.server"               # Generated X server cookie

  # X in container
  Xcontainer="auto"
  Xcontainerbackend=""
  Xcontainercommand=""
  Xcontainerimage="x11docker/xserver"
  Xcontainerip=""
  Xcontainername=""
  Xcontainer_supportlist="--xpra --xpra2 --xpra2-xwayland --xephyr --weston-xwayland --xvfb --weston --xwayland --nxagent"  # --kwin works, but too big for image
  Xtoolscontainer="no"

  # Window manager
  Windowmanagermode="none"                        # --wm: Window manager to use: container/host/auto
  Windowmanagercommand=""                         # --wm: Argument for --wm or host wim command

  # Host integration
  Alsacard="$ALSA_CARD"                           # --alsa: Specified ALSA card
  Hosthomebasefolder=""                           # --homebasedir: Base directory for container home with --home
  Langwunsch=""                                   # --lang: Search or create UTF-8 locale in container and set LANG
  Pulseaudioconf="pulseaudio.client.conf"         # --pulseaudio: Client config in container
  Pulseaudiocookie="pulseaudio.cookie"            # --pulseaudio: possible pulse cookie from host to share
  Pulseaudiomode=""                               # --pulseaudio: 'tcp', 'socket' or 'auto'
  Pulseaudiomoduleid=""                           # --pulseaudio: module ID, stored for unload in finish()
  Pulseaudioport=""                               # --pulseaudio: TCP port for --pulseaudio=tcp
  Pulseaudiosocket="pulseaudio.socket"            # --pulseaudio: unix socket for --pulseaudio=socket
  Sharealsa="no"                                  # --alsa: enable ALSA sound, share /dev/snd
  Shareclipboard="no"                             # --clipboard: Enable clipboard sharing
  Sharecupsmode=""                                # --printer: Share access to CUPS printer server: socket|tcp|""
  Sharegpu="no"                                   # --gpu: Use hardware accelerated OpenGL, share files in /dev/dri
  Sharehome="no"                                  # --home: Share a folder ~/.local/share/x11docker/Imagename with created container
  Sharevolumes=""                                 # --share: Host files or folders or devices to share, array
  Sharevolumescount="0"                           # --share: Counts shared folders in array
  Sharewebcam="no"                                # --webcam: Share webcam device /dev/video*
  Virgl=""                                        # --gpu=virgl: GPU access through virgl gallium driver

  # Image information
  Imagearchitecture=""
  Imagecommand=""
  Imageentrypoint=""
  Imageinspect=""                                 # Output of --inspect
  Imageworkdir=""
  Imageuser=""

  # Container setup
  Adminusercaps="no"                              # --cap-default, --sudouser, --init=systemd: add capabilities for general container system administration
  Allownewprivileges="auto"                       # --newprivileges: Docker run option --security-opt=no-new-privileges. Default: no. Enabled by options --newprivileges, --cap-default, --sudouser.
  Backend=""                                      # --backend: Backend to use, like docker, podman, nerdctl, proot, host. A default can be specified with default preset file.
  Backendbin=""                                   # path to binary of backend
  Backendinfo=""                                  # output of 'docker info'
  Capabilities=""                                 # Capabilities to add. Default: none, exceptions for --init, --sudouser
  Capdropall="yes"                                # --cap-default: Drop all container capabilities and set --securty-opt=no-new-privileges yes/no
  Chrootmountlist=""
  Cmdexec=""
  Containercommand=""                             # Container command [+args]
  Containerentrypoint=""
  Containerenvironment=""                         # --env: Environment variables
  Containerenvironmentcount="0"
  Containerenvironmentfile="container.environment" # file to store final container environment
  Containerlocaltimefile="libc.localtime"         # localtime file from host shared to container
  Containername=""                                # --name: Container name
  Containersetup="yes"
  Customdockeroptions=""                          # -- [...] -- : Custom options for "docker run".
  Defaultruntime=""
  Imagename=""                                    # Image to run
  Interactive="no"                                # --interactive: Run docker with interactive tty yes/no
  Limitresources=""                               # --limit: Limit access to CPU and RAM, 0.1 ... 1.0
  Network="none"                                  # --network
  Noentrypoint="no"                               # --no-entrypoint: Disable entrypoint in image yes/no
  Optionset=""                                    # list of set options for later support check
  Optionsetall=""                                 # list of all available long options
  Remountcgroup=""                                # --init=systemd with cgroupv2 in docker workaround
  Rootfs=""                                       # --backend=proot
  Rootlessbackend=""                              # Check for rootful/rootless docker depending on DOCKER_HOST
  Runtime=""                                      # Runtime to use. runc|nvidia|kata-runtime|crun
  Snapsupport=""                                  # --snap: Fallback mode to support docker in snap
  Shareipc="no"
  Stopsignal=""                                   # Signal to send on 'docker stop'
  Sudouser=""                                     # --sudouser: Create user with sudo permissions and root user with password 'x11docker'
  Switchcontaineruser="no"                        # --init=systemd|openrc|runit|sysvinit: User switching to trigger login services yes/no
  Switchcontainerusercaps="no"                    # --init=systemd|openrc|runit|sysvinit, --sudouser: Add capabilities for su/sudo user switching
  Systemdjournallogfile="systemd.journal.log"
  Workdir=""                                      # --workdir: Set working directory in container

  # Init and DBus
  Dbusrunsession="no"                             # --dbus, --wayland, --init=systemd|openrc|runit|sysvinit: Run container command with dbus-run-session / DBus user session
  Dbussystem="no"                                 # --init=systemd|openrc|runit|sysvinit: Run DBus system daemon in container
  Initsystem="tini"                               # --init: Init system in container
  Sharecgroup="no"                                # --sharecgroup, --init=systemd: share /sys/fs/cgroup. Also needed for elogind
  Sharehostdbus="no"                              # --hostdbus: Connect to DBus user daemon on host
  Tinibinaryfile=""                               # --init=tini (default): Binary of tini; either /usr/bin/docker-exec or provided by user in [...]/share/x11docker
  Tinicontainerpath="/usr/local/bin/init"         # --init=tini: Path of tini (or catatonit) in container

  # Custom additional commands
  Runasuser=""                                    # --runasuser: Add container command to containerrc
  Runasroot=""                                    # --runasroot: Add container command to container setup script running as root
  Runfromhost=""                                  # --runfromhost: Add host command to xinitrc

  # Miscellaneous
  Buildimage=""                                   # --build: x11docker image to build from repo Dockerfile
  Cachenumber="$(date +%s%N | cut -c6-16)"        # Number to use for cache folder
  [ -z "$Cachenumber" ] && Cachenumber="$(makecookie)"
  Codename=""                                     # created from image name and command without special chars for use with container name and cache folder
  Fallback="yes"                                  # --fallback: Allow or deny fallbacks for failing options.
  Imagebasename=""                                # Image name without tags and / replaced with -. For use of --home folders.
  Parsedoptions_global=""                         # Parsed options
  Presetdirlocal="$HOME/.config/x11docker/preset" # --preset storage dir (local)
  Presetdirsystem="/etc/x11docker/preset"         # --preset storage dir (system)
  Presetlist=""                                   # List of already parsed preset files to avoid a loop
  Preservecachefiles="no"                         # If yes, don't delete cache files on exit. For few failure cases only.

  # Verbosity options
  Debugmode="no"                                  # --debug: Excerpt of --verbose, also bash error checks
  Showcontainerid=""                              # --printid: Output of container ID on stdout
  Showcontaineroutput="yes"                       # Show container command stdout
  Showcontainerpid1pid=""                         # --printpid1: Output of host PID of container PID 1 on stdout
  Showdisplayenvironment=""                       # --printenv: Output of environment variables of new display on stdout
  Showinfofile=""                                 # --printinfofile: Show path of $Storeinfofile
  Silent="no"                                     # --quiet: Do not show x11docker messages
  Verbose=""                                      # --verbose: Be verbose yes/no
  Verbosecolors="no"                              # -V: colored output for --verbose (and delete some noisy systemd error messages)
  Wikipackages="You can look for the package name of this command at:
 https://github.com/mviereck/x11docker/wiki/dependencies#table-of-all-packages"
  Wikipackagesimage="$Wikipackages
 Alternatively you can provide image x11docker/xserver."

  # Special options not starting X or docker
  Cleanup="no"                                    # --cleanup: Remove orphaned containers and cache files
  Createlauncher="no"                             # --launcher: Create application launcher on desktop and exit yes/no
  Installermode=""                                # --install/--update/--update-master/--remove

  # Lists of window managers
  # - these window managers are known to work well with x11docker (alphabetical order)(excluding $Wm_not_recommended and $Wm_ugly):
  Wm_good="amiwm blackbox cinnamon compiz ctwm enlightenment fluxbox flwm fvwm"
  Wm_good="$Wm_good jwm kwin kwin_x11 lxsession mate-session mate-wm marco metacity notion olwm olvwm openbox ororobus pekwm"
  Wm_good="$Wm_good sawfish twm wmaker w9wm xfwm4"
  # - these wm's are recommended and lightweight, but cannot show desktop options. best first:
  Wm_recommended_nodesktop_light="metacity marco openbox sawfish xfwm4"
  # - these wm's are recommended and heavy, but cannot show desktop options (especially exiting themselves). best first:
  Wm_recommended_nodesktop_heavy="kwin compiz"
  # - these wm's are recommended, lightweight AND desktop independent. best first:
  Wm_recommended_desktop_light="flwm blackbox fluxbox jwm mwm wmaker afterstep amiwm fvwm ctwm pekwm olwm olvwm"
  # - these wm's are recommended, heavy AND desktop independent. best first:
  Wm_recommended_desktop_heavy="lxsession mate-session enlightenment cinnamon cinnamon-session plasmashell"
  # - these wm's are not really useful (please don't hit me) (best first):
  Wm_not_recommended="awesome evilwm herbstluftwm i3 lwm matchbox miwm mutter spectrwm subtle windowlab wmii wm2"
  # - these wm's cannot be autodetected by wmctrl if they are already running
  Wm_nodetect="aewm aewm++ afterstep awesome ctwm mwm miwm olwm olvwm sapphire windowlab wm2 w9wm"
  # - these wm's can cause problems (they can be beautiful, though):
  Wm_ugly="icewm sapphire aewm aewm++"
  # - these wm's doesn't work:
  Wm_bad="budgie-wm clfswm tinywm tritium muffin gnome-shell"
  # List of all working window managers, recommended ones first, excluding $Wm_bad:
  Wm_all="$Wm_recommended_nodesktop_light $Wm_recommended_nodesktop_heavy  $Wm_recommended_desktop_light $Wm_recommended_desktop_heavy $Wm_good $Wm_ugly $Wm_not_recommended $Wm_nodetect"

  # x11docker communication functions to integrate into generated scripts
  Messagefifofuncs='
warning() {
  echo "$*:WARNING"   >>$Messagefile
}
note() {
  echo "$*:NOTE"      >>$Messagefile
}
verbose() {
  echo "$*:VERBOSE"   >>$Messagefile
}
debugnote() {
  echo "$*:DEBUGNOTE" >>$Messagefile
}
error() {
  echo "$*:ERROR"     >>$Messagefile
  exit 64
}
stdout() {
  echo "$*:STDOUT"    >>$Messagefile
}'
  MessagefifofuncsX='
warning() {
  echo "$*:WARNING"   | sed "s/\$/ /" >>$Messagefile
}
note() {
  echo "$*:NOTE"      | sed "s/\$/ /" >>$Messagefile
}
verbose() {
  echo "$*:VERBOSE"   | sed "s/\$/ /" >>$Messagefile
}
debugnote() {
  echo "$*:DEBUGNOTE" | sed "s/\$/ /" >>$Messagefile
}
error() {
  echo "$*:ERROR"     | sed "s/\$/ /" >>$Messagefile
  exit 64
}
stdout() {
  echo "$*:STDOUT"    | sed "s/\$/ /" >>$Messagefile
}'
  Messagefifofuncs_escaped='
warning() {
  echo \"\$*:WARNING\"   >>\$Messagefile
}
note() {
  echo \"\$*:NOTE\"      >>\$Messagefile
}
verbose() {
  echo \"\$*:VERBOSE\"   >>\$Messagefile
}
debugnote() {
  echo \"\$*:DEBUGNOTE\" >>\$Messagefile
}
error() {
  echo \"\$*:ERROR\"     >>\$Messagefile
  exit 64
}
stdout() {
  echo \"\$*:STDOUT\"    | sed \"s/\\\$/ /\" >>\$Messagefile
}'
  Messagefifofuncs_escapedX='
warning() {
  echo \"\$*:WARNING\"   | sed \"s/\\\$/ /\"  >>\$Messagefile
}
note() {
  echo \"\$*:NOTE\"      | sed \"s/\\\$/ /\" >>\$Messagefile
}
verbose() {
  echo \"\$*:VERBOSE\"   | sed \"s/\\\$/ /\" >>\$Messagefile
}
debugnote() {
  echo \"\$*:DEBUGNOTE\" | sed \"s/\\\$/ /\" >>\$Messagefile
}
error() {
  echo \"\$*:ERROR\"     | sed \"s/\\\$/ /\" >>\$Messagefile
  exit 64
}
stdout() {
  echo \"\$*:STDOUT\"    | sed \"s/\\\$/ /\" >>\$Messagefile
}'
}
unpriv() {                      # run a command as unprivileged user. Needed if x11docker was started by root or with sudo.
  # $Unpriv is declared in check_hostuser: 'eval' or 'su $Hostuser -c'
  $Unpriv "${1:-}"
}
main() {
  trap finish EXIT
  trap finish_sigint SIGINT

  exec {FDstderr}>&2            # stderr channel for warning(), note(), debugnote() and --verbose

  declare_variables
  parse_options "$@"
  [ "$Silent" = "yes" ]    && exec {FDstderr}>/dev/null                           # --quiet
  [ "$Debugmode" = "yes" ] && {                                                   # --debug
    set -Eu
    trap 'traperror $? $LINENO $BASH_LINENO "$BASH_COMMAND" $(printf "::%s" ${FUNCNAME[@]})'  ERR
  }
  [ -n "$Containeruserpassword" ] && {                                            # --password
    check_optionset "--password" "$(grep -v -- '--password' <<< "$Optionsetall")" ||:
    set_password "$Containeruserpassword"
    finish
  }
  check_host                     # get some infos about host system #time0,234
  check_options_arguments        # check for valid arguments
  check_hostuser                 # find unprivileged host user                    # --hostuser
  create_cachefiles              # create cache files owned by unprivileged user  # --cachebasedir
  setup_verbosity                # create [and show] summary logfile              # --verbose

  # Special x11docker jobs
  [ "$Createlauncher" = "yes" ] && {
    create_launcher                                                               # --launcher: Create application launcher icon on desktop
    finish
  }
  [ "$Cleanup" = "yes" ] && {                                                     # --cleanup: Clean up cache and orphaned x11docker containers
    check_optionset "--cleanup" "$(grep -v -E -- '--cleanup|--backend' <<< "$Optionsetall")" ||:
    cleanup
    finish
  }
  [ "$Installermode" ] && {                                                       # --install, --update, --update-master, --remove
    check_optionset "$Installermode" "$(grep -v -E -- '--install|--update|--update-master|--remove' <<< "$Optionsetall")" ||:
    installer "$Installermode"
    finish
  }
  [ "$Buildimage" ] && {                                                          # --build: Build image from x11docker repository
    check_optionset "--build" "$(grep -v -E -- '--build|--backend' <<< "$Optionsetall")" ||:
    buildimage "$Imagename"
    finish
  }

  check_hostxenv                 # check X environment from host
  check_snap                     # check if docker is installed in snap; causes restrictions
  check_containeruser            # unprivileged user in container                 # --user
  check_xcontainer               # check if x11docker/xserver can/will be used    # --xc
  check_xserver                  # check chosen X server or auto-choose one
  check_containerhomebasedir     # check base dir for --home and possible rootfs  # --homebasedir
  check_backend                                                                   # --backend
  check_options_interferences    # check options, change settings if needed
  check_containerhome            # create persistent container home               # --home
  check_xdg_runtime_dir
  check_options_messages         # some messages depending on options, but not changing anything
  setup_fifo                     # open message channels for container, xinitrc and watchpidlist()
  check_screensize               # size of host X and of new X server             # --size #time0,213
  [ "$Windowmanagermode" ]       && check_windowmanager                           # --wm
  [ "$Sharegpu" = "yes" ]        && setup_gpu ||:                                 # --gpu
  [ "$Sharewebcam" = "yes" ]     && setup_webcam                                  # --webcam
  [ "$Sharecupsmode" ]           && setup_printer                                 # --printer
  [ "$Pulseaudiomode" ]          && setup_sound_pulseaudio                        # --pulseaudio
  [ "$Sharealsa" = "yes" ]       && setup_sound_alsa                              # --alsa
  [ "$Cleanxhost" = "yes" ]      && clean_xhost                                   # --clean-xhost
  [ "$Sharehostdbus" = "yes" ]   && setup_hostdbus                                # --hostdbus

  #### Create command to run X server [and/or Wayland compositor]
  check_newxenv                  # find free display
  { [ "$Xcontainer" = "yes" ] || [ "$Xtoolscontainer" = "yes" ] ; } && {          # --xc
    create_xcontainercommand
    debugnote "X container command:
  $Xcontainercommand"
    start_xcontainer
  }
  store_newxenv
  [ "$Shareclipboard" = "yes" ]  && setup_clipboard ||:                           # --clipboard
  [ "$Xserver" != "--xorg" ]     && [ -n "$Newxvt" ] && note "Option --vt only takes effect with option --xorg."
  [ "$Xserver" = "--xorg" ]      && check_vt                                      # --vt: find free tty/virtual terminal for Xorg
  create_xcommand                # set up start command for X server              # all X server and Wayland options
  [ "$Xcommand" ]                && debugnote "X server command:
  $Xcommand"
  [ "$Compositorcommand" ]       && debugnote "Compositor command:
  $Compositorcommand"

  storeinfo "x11dockerpid=$$"    # store pid of x11docker
  debugnote "x11docker version:  $Version
  Backend version:               $(${Backendbin:-:} --version 2>&1)
  OCI Runtime:                   $Runtime
  Host system:                   $(grep '^PRETTY_NAME' /etc/os-release 2>/dev/null | cut -d= -f2 || echo "$Hostsystem")
  Host architecture:             $Hostarchitecture
  Command:
    '$0' $(for Line in "$@"; do echo -n "'$Line' " ; done)
  Parsed options:
    $Parsedoptions_global
  x11docker was started by:      $Startuser
  As host user serves:           $Hostuser
  Container user will be:        $( [ "$Createcontaineruser" = "yes" ] && echo "$Containeruser" || echo "(retaining USER of image)")
  Container user password:       $( [ "$Createcontaineruser" = "yes" ] && echo "x11docker"      || echo "(unknown)")
  Running in a terminal:         $Runsinterminal
  Running on console:            $Runsonconsole
  Running over SSH:              $Runsoverssh
  Running sourced:               $Runssourced
  bash \$-:                       $-"
  [ "$Winsubsystem" ] && debugnote "
  Running on Windows subsystem:  $Winsubsystem
  Path to subsystem:             $(convertpath windows "$Winsubpath")/
  Mount path in subsystem:       $Winsubmount/
  Using MobyVM:                  $Mobyvm"

  setup_initsystem               # init in container.                             # --init
  case "$Backend" in
    docker|podman|nerdctl)
      check_image
      setup_capabilities         # add linux capabilities if needed for some options. Default: --cap-drop=ALL
    ;;
  esac
  case "$Backend" in
    proot|chroot|docker|podman|nerdctl)
      create_backendcommand           # create 'docker run' command
      echo "$Backendcommand" >> "$Backendcommandfile"
      debugnote "$Backend command:
  $Backendcommand"
      [ "$Containersetup" = "yes" ] && {
        ## containerrootrc runs as root in container.
        # Main jobs: create unprivileged container user, disable possible privilege leaks, set local time.
        # Optional jobs: run init system, run DBus daemon, install nvidia driver, create language locale.
        create_containerrootrc >> "$Containerrootrc"
        verbose "Generated containerrootrc:
$(nl -ba <$Containerrootrc)"
      }
    ;;
  esac
  [ "$Containersetup" = "yes" ] && {
    create_containerrc
    verbose "Generated containerrc:
$(nl -ba <$Containerrc)"
    create_cmdrc
    verbose "Generated cmdrc:
$(nl -ba <$Cmdrc)"
  }

  { #### Run docker image
    # For code flow logic, start_xserver() should run here first and be moved to background.
    # For technical reasons, xinit must not run in a subshell:
    #   --xorg on tty only works if xinit runs in foreground to grab the tty.
    #   Otherwise, Xwrapper.config must be edited to 'allowed_users=anybody' even on console.
    # Thus docker runs in this subshell after X server is ready to accept connections.

    # softlink to X or Wayland unix socket in container                           # --xc
    case "$Xcontainer" in
      yes)
        [ "$Newdisplay" ]       && ln -s "$Cachefolder/tmp/.X11-unix/X$Newdisplaynumber"  "/tmp/.X11-unix/X$Newdisplaynumber"
        [ "$Newwaylandsocket" ] && ln -s "$Cachefolder/XDG_RUNTIME_DIR/$Newwaylandsocket" "$XDG_RUNTIME_DIR/$Newwaylandsocket"
      ;;
    esac

    # wait for X to be ready
    waitforlogentry 'start_container()' $Xinitlogfile 'xinitrc is ready' "$Xiniterrorcodes"
    rocknroll || timetosaygoodbye main

    [ "$Runfromhost" ] && {                                                       # --runfromhost
      while read Line; do
        unpriv "env $Newxenv $Runfromhost"
      done <<< "$Runfromhost"
    }

    case "$Backend" in
      chroot)
        Chrootmountlist="$(grep . <<< "$Chrootmountlist")"
        while read -r Line; do
          debugnote "--backend=$Backend: mount/ln $Line"
          Source="$(cut -d'|' -f1 <<< "$Line")"
          Target="$(cut -d'|' -f2 <<< "$Line")"
          [ -d "$Source" ] && {
            mkdir -v -p "$Target"
            mount --bind "$Source" "$Target" 2>> "$Containerlogfile" || error "--backend=$Backend: mount failed.
  Last lines of log:
$(tail "$Containerlogfile")"
          }
          [ -S "$Source" ] && ln "$Source" "$Target"
          [ -f "$Source" ] && ln "$Source" "$Target"
        done <<< "$Chrootmountlist"
#        mount -t devpts devpts "$Rootfs/dev/pts" || error "--backend=$Backend: mount of devpts failed."
      ;;
    esac

    # start container
    start_container
    Pid1pid="$(storeinfo dump pid1pid)"

    # watch container
    case "$Winsubsystem" in
      "") setonwatchpidlist "${Pid1pid:-NOPID}" pid1pid ;;
      *)  setonwatchpidlist "CONTAINER$Containername" ;;
    esac

    # watch xinit and X server
    case "$Xserver" in
      --tty|--hostdisplay|--hostwayland|--weston|--kwin) ;;
      *)
        Xinitpid="$(pgrep -a xinit 2>/dev/null | grep "xinit $Xinitrc" | awk '{print $1}')"
        checkpid "$Xinitpid"   && setonwatchpidlist "$Xinitpid" xinit
        Xserverpid=$(ps aux | rmcr | grep "$(echo "${Xcommand:-nothingtolookfor}" | cut -d' ' -f1-2)" | grep -v grep | grep -v xinit | awk '{print $2}')
        checkpid "$Xserverpid" && setonwatchpidlist "$Xserverpid" Xserver
      ;;
    esac

    [ "$Pulseaudiomode" = "tcp" ] && start_pulseaudiotcp                          # --pulseaudio=tcp

    # some debug output
    checkpid "$Pid1pid" && debugnote "Process tree of container: (maybe not complete yet)
$(pstree -cp "$Pid1pid" 2>&1 ||:)"
    debugnote "Process tree of x11docker:
$(pstree -p $$ 2>&1 ||:)"
    debugnote "storeinfo(): Stored info:
$(cat $Storeinfofile)"
    debugnote "storepid(): Stored pids:
$(cat $Storepidfile)"

    # optional info on stdout
    case "$Showinfofile" in                                                       # --printinfofile
      "") ;;
      yes) echo "$Storeinfofile" ;;
      *)   unpriv "echo '$Storeinfofile' > '$Showinfofile'"         || error "Option --printinfofile: Invalid file $Showinfofile" ;;
    esac
    case "$Showdisplayenvironment" in                                             # --printenv
      "") ;;
      yes) storeinfo dump Xenv ;;
      *)   unpriv "echo '$Newxenv_cr' > '$Showdisplayenvironment'"  || error "Option --printenv: Invalid file $Showdisplayenvironment" ;;
    esac
    case "$Showcontainerid" in                                                    # --printid
      "") ;;
      yes) storeinfo dump containerid ;;
      *)   unpriv "storeinfo dump containerid > '$Showcontainerid'" || error "Option --printid: Invalid file $Showcontainerid" ;;
    esac
    case "$Showcontainerpid1pid" in                                               # --printpid1
      "") ;;
      yes) echo "$Pid1pid" ;;
      *)   unpriv "echo '$Pid1pid' > '$Showcontainerpid1pid'"       || error "Option --printcontainerpid1pid: Invalid file $Showcontainerpid1pid" ;;
    esac
    storeinfo "x11docker=ready"
  } <&0 & storepid $! containershell

  ## Create helper script xinitrc to set up X
  # xinitrc is started by xinit and does some setup within new X server.
  # Main job: create cookie, check xhost, set keyboard layout.
  # Optional jobs: run window manager, run xfishtank, run host command, share clipboard, scale/rotate --xorg, create set of screen resolutions.
  rocknroll && {
    create_xinitrc >> "$Xinitrc"
    verbose "Generated xinitrc:
$(nl -ba <$Xinitrc)"
    [ -s "$Westonini" ] && verbose "Generated weston.ini:
$(nl -ba <$Westonini)"
  }

  case "$Xserver" in
    --xpra*)
      {
        waitforlogentry xpra $Storeinfofile "xinitrc=ready" infinity
        rocknroll && start_xpra                                                   # --xpra, --xpra-xwayland
      } & storepid $! xpraloop
    ;;
  esac
  rocknroll && [ "$Compositorcommand" ] && start_compositor
  rocknroll && start_xserver

  saygoodbye main
}

main "$@"
